commit 28d4d40b0cec220f88e4c65e288f7f1f6ebc76ed Author: review512jwy@163.com <“review512jwy@163.com”> Date: Fri Oct 31 11:22:38 2025 +0800 同步代码 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..04115ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ +logs + +### VS Code ### +.vscode/ + +### MAC ### +.DS_Store \ No newline at end of file diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..a45eb6b --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..ffdc10e --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0d216e7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM registry.ap-northeast-1.aliyuncs.com/southwave/jdk17-template:latest +WORKDIR /app +COPY target/data-center-sender.jar app.jar +EXPOSE 8201 + + +# 使用UseCGroupMemoryLimitForHeap +# ENV JAVA_OPTS="-Xms1g -Xmx2g -XX:+UseParallelGC -XX:ParallelGCThreads=2 -XX:MaxGCPauseMillis=200 -XX:GCTimeRatio=19 -XX:NewRatio=3 -XX:+AlwaysPreTouch -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/app/gc.log" +# ENV JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1NewSizePercent=20 -XX:G1MaxNewSizePercent=60 -XX:InitialHeapSize=2g -Xmx4096m -XX:MetaspaceSize=256m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/app/gc.log" +# 使用shell方式的ENTRYPOINT来确保环境变量被展开 +ENTRYPOINT java $JAVA_OPTS -jar app.jar --spring-profiles=$env \ No newline at end of file diff --git a/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.docx b/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.docx new file mode 100644 index 0000000..84fc000 Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.docx differ diff --git a/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.pdf b/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.pdf new file mode 100644 index 0000000..f5afcd9 Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI SaaSプラットフォーム API説明書-ver 2.1.pdf differ diff --git a/Documents/传感器资料/NBI API 资料/NBIInterfaceTest.zip b/Documents/传感器资料/NBI API 资料/NBIInterfaceTest.zip new file mode 100644 index 0000000..337470c Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBIInterfaceTest.zip differ diff --git a/Documents/传感器资料/NBI API 资料/NBI字段匹配表.xlsx b/Documents/传感器资料/NBI API 资料/NBI字段匹配表.xlsx new file mode 100644 index 0000000..a0cbd62 Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI字段匹配表.xlsx differ diff --git a/Documents/传感器资料/NBI API 资料/NBI数据解析文档.docx b/Documents/传感器资料/NBI API 资料/NBI数据解析文档.docx new file mode 100644 index 0000000..6e7efc9 Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI数据解析文档.docx differ diff --git a/Documents/传感器资料/NBI API 资料/NBI数据解析文档.pdf b/Documents/传感器资料/NBI API 资料/NBI数据解析文档.pdf new file mode 100644 index 0000000..9b920c6 Binary files /dev/null and b/Documents/传感器资料/NBI API 资料/NBI数据解析文档.pdf differ diff --git a/Documents/传感器资料/ZETA传感器/41-干接点检测器-OCZ1ZT.docx b/Documents/传感器资料/ZETA传感器/41-干接点检测器-OCZ1ZT.docx new file mode 100644 index 0000000..959f491 Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/41-干接点检测器-OCZ1ZT.docx differ diff --git a/Documents/传感器资料/ZETA传感器/57-多路干接点检测器-OC16ZT.docx b/Documents/传感器资料/ZETA传感器/57-多路干接点检测器-OC16ZT.docx new file mode 100644 index 0000000..0261a9c Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/57-多路干接点检测器-OC16ZT.docx differ diff --git a/Documents/传感器资料/ZETA传感器/59-485采集终端-D485ZT.docx b/Documents/传感器资料/ZETA传感器/59-485采集终端-D485ZT.docx new file mode 100644 index 0000000..4504670 Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/59-485采集终端-D485ZT.docx differ diff --git a/Documents/传感器资料/ZETA传感器/BLE gateway/ZETA®LPWAN_BLEZETAGW_产品规格_3.0版.docx b/Documents/传感器资料/ZETA传感器/BLE gateway/ZETA®LPWAN_BLEZETAGW_产品规格_3.0版.docx new file mode 100644 index 0000000..ad26ab9 Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/BLE gateway/ZETA®LPWAN_BLEZETAGW_产品规格_3.0版.docx differ diff --git a/Documents/传感器资料/ZETA传感器/JAZE CO2+温湿度.pdf b/Documents/传感器资料/ZETA传感器/JAZE CO2+温湿度.pdf new file mode 100644 index 0000000..3a0e00e Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/JAZE CO2+温湿度.pdf differ diff --git a/Documents/传感器资料/ZETA传感器/JAZE 超声波距离传感器使用手册v1.1.docx b/Documents/传感器资料/ZETA传感器/JAZE 超声波距离传感器使用手册v1.1.docx new file mode 100644 index 0000000..09a842e Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/JAZE 超声波距离传感器使用手册v1.1.docx differ diff --git a/Documents/传感器资料/ZETA传感器/JAZE 超音波距離センサ_JZUS91A1_仕様書_V1.1_20210622.pdf b/Documents/传感器资料/ZETA传感器/JAZE 超音波距離センサ_JZUS91A1_仕様書_V1.1_20210622.pdf new file mode 100644 index 0000000..437fda7 Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/JAZE 超音波距離センサ_JZUS91A1_仕様書_V1.1_20210622.pdf differ diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/5G NBIOT体温定位健康手环.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/5G NBIOT体温定位健康手环.docx new file mode 100644 index 0000000..e9fc67a Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/5G NBIOT体温定位健康手环.docx differ diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M & NBIOT Smart Band.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M & NBIOT Smart Band.docx new file mode 100644 index 0000000..a3ef72f Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M & NBIOT Smart Band.docx differ diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M, NBIOT体温监测健康定位手环.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M, NBIOT体温监测健康定位手环.docx new file mode 100644 index 0000000..0e6aa69 Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/产品/BG77製品/B2316 CAT-M, NBIOT体温监测健康定位手环.docx differ diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/平台,APP和数据对接简介-20210127.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/平台,APP和数据对接简介-20210127.docx new file mode 100644 index 0000000..e108fa5 Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/平台,APP和数据对接简介-20210127.docx differ diff --git a/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/欧孚通信设备通信协议json1127.docx b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/欧孚通信设备通信协议json1127.docx new file mode 100644 index 0000000..3b2498c Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/Oviphone健康手环/平台、数据格式/欧孚通信设备通信协议json1127.docx differ diff --git a/Documents/传感器资料/ZETA传感器/skytech/SZC100(CO2+温湿度センサー)取扱い説明書_Rev00.pdf b/Documents/传感器资料/ZETA传感器/skytech/SZC100(CO2+温湿度センサー)取扱い説明書_Rev00.pdf new file mode 100644 index 0000000..f2b2bd7 Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/skytech/SZC100(CO2+温湿度センサー)取扱い説明書_Rev00.pdf differ diff --git a/Documents/传感器资料/ZETA传感器/skytech/SZT200(高精度傾斜計)取扱い説明書_Rev02.pdf b/Documents/传感器资料/ZETA传感器/skytech/SZT200(高精度傾斜計)取扱い説明書_Rev02.pdf new file mode 100644 index 0000000..44bab7e Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/skytech/SZT200(高精度傾斜計)取扱い説明書_Rev02.pdf differ diff --git a/Documents/传感器资料/ZETA传感器/skytech/SZW100_センサーデータフォーマット_r05.pdf b/Documents/传感器资料/ZETA传感器/skytech/SZW100_センサーデータフォーマット_r05.pdf new file mode 100644 index 0000000..b7f94e9 Binary files /dev/null and b/Documents/传感器资料/ZETA传感器/skytech/SZW100_センサーデータフォーマット_r05.pdf differ diff --git a/Documents/传感器资料/设备匹配表/NESIC租户KTC项目设备列表_20210805.xlsx b/Documents/传感器资料/设备匹配表/NESIC租户KTC项目设备列表_20210805.xlsx new file mode 100644 index 0000000..4be10e0 Binary files /dev/null and b/Documents/传感器资料/设备匹配表/NESIC租户KTC项目设备列表_20210805.xlsx differ diff --git a/TECHSOR_OFFICIAL.pem b/TECHSOR_OFFICIAL.pem new file mode 100644 index 0000000..d9e9076 --- /dev/null +++ b/TECHSOR_OFFICIAL.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAoXT+WI0CFw06NjKINFhz5y9xs9CeQYHN0+G24fIePPEBPRhW +Faxg36GuFuYKxk59W+Ey8w5Stz8R1wckbU//uaio/P7QhNbl+OpVIKbU93SVW8x6 +VH8roTSf+uYh6k6qO5ejAjqiicuEj8dZ8fQWyYSB4X/mLrXg5k5dxKyjhv5nM8RO +vvFHy/uvcdFOAeBbYrq9pHOkSP3BLU+wjutBDmxEdM9YLJk9qOM9bulxr1+QMXNY +4pjpWDlqfM1BpZZDhKzvUqLVjO+QwQ2E8mUgoK2PVY1umC5X8Jlcfy83A77+pKzl +ZKYpLgfLHj7rV5I12uoBx4mvLO1jw6XrNlIb9QIDAQABAoIBAGe+pDxEBwbG4hO3 +LpvsBjWT38y6DSZsgNRX4cqXZ+Siu7gFLjNo+ypXWmSuVlgMUTK7pqBVIMNMjGsN +1NNEpz4l6Mf/9/6Tk1v5Ps/nQ0rqJ5q/7g7jVCaWiQGP5FUJTQtTqVOiV5SRKFG2 +t83nmMjOEyLRqxdymNuDmW7pu33ebtDZxwm+QeN3nz6TwAbV7Geas5NC1UJLR0+C +nLQh0M9QELv7fKUPiznr6wE9CxB+1fx9es6HJxYK6Q4W+4mjd8CVXXMPDiQhTVEt +jnibk3UfxBQB7fkYP8PUukGq+Z4BgCszoAW0J8gFwiC8YMuzzQw3k83vPCiZZ+JQ +u0hgUPUCgYEA1lQi4mhKNtYMfbVtKEURajvUy5pZw4rzIyo+6XFjE25c3Q/nt3ot +d+vgh6dNCfRll95CB2RKOUoelj3nDeX+aM+QEwwQghLAtAdcRcIhG3DfIYBOJmiw +ugjbk5AW1bc38eQsjtRD7aZMyJjjr2bI7D2D5/MrjqdMZ6sxYdFRmpMCgYEAwNlB +h/Da5kY/z9qcXSnysr78ffU5BLGcxN2At+aXYGKyoR2YQqSME0y1jC9h0w+a7nAx +jCtkz+ozhBkP7o0vRZ8eGbP/65CbPrsY8P9Kpvk7dr2IRMTUjMFihhptGQFrLP3a +g+T2T+gQWS0v5HqiSot6znHZL+jND9elwx8nnFcCgYEAuemhmOL9/TMPArwtQ5El +2hCsNTBeTNBqt0Yd7ED+wAwrYVY6mVzRtARXb1Qf71KgDWwtulu0Rp2Uip6Hnfaz +CBeD0gHVD/9USNVZpOkP7s2pv1WcdJS7N6QXU5jZNekIDjruq7ZUdgCa+iYk2jE+ +eC2kDb9RORzFmedVnpQDRSECgYBH26xTXyfxzhNQ/ABvpoXMnOWweYN5gEUOBgtE +eyPEwoIVDtYBXxbiyh6L0cv9vT7Zwex0cmbqIjZ37m7FUM5gft3UbgHaYNO4GDc+ +9aF3fj7uC8mO9ljM6fIwTgCA5MpuxVh69QHi3HHbCL9jv15hsH9eFYX8GB7w3EXj +4uP7mQKBgQCFG7l/s1VDsLn9VNpkoUBjZMMdrLCyCWVrTEdeYtZ5LIx3etZxgbah +/rvryIDgc/j7riQgEDnqYk19Ee/HVxK1duJO6d/ywDcSlnNMaChrS8khsMrbK6yI +geqH+9jaaPUVacfeVe0MCIGLxnMiUucIUIyp3VV2OuJ2xx68xqw1wA== +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/cmd b/cmd new file mode 100644 index 0000000..a5abb79 --- /dev/null +++ b/cmd @@ -0,0 +1 @@ +docker run -d --name test-sender -e redisTimeout=30000 -e redisMaxActive=7000 -e iotcorePort=8883 -e secretkey=Plkid7RDnHc1gGbp2yAv/Scc+ukI0q8vzBuyEBN2 -e businessQueryPushInfoUrl=https://iothub-web-stg.ttkdatatechbuild.com/api/targetConfig/config/v1/queryAlertForwardConfigByAlarmTmplIds -e alarmEmailResultUrl=https://iothub-web-stg.ttkdatatechbuild.com/api/common/logEmailSentResult -e dynamicJdbcUrl=jdbc:mysql://rm-bp11k2zm2fr7864428o.mysql.rds.aliyuncs.com/%s -e defaultRedisCacheTTL=5000 -e JAVA_OPTS="-XX:+UseZGC -Xms1g -Xmx2g -XX:MetaspaceSize=256m -Xlog:gc*:file=/app/gc.log:time,level,tags" -e iotcoreClientId=tkbuild-demo -e jdbcUsername=zhc -e redisShutdownTimeout=30000 -e roidAuthorization="Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..." -e apihost=https://api-sec.test-public-api.kanri-roid.app -e messageProtocol=kinesis -e redisLockExpire=500 -e logLevel=DEBUG -e awsaccesskey=AKIA5OFH5OOZHM3U3KX4 -e dataCenterSenderTargetUrl=http://43.163.243.201:8030/nesic/deviceId/ -e queryPushInfoUrl=https://iothub-web-stg.ttkdatatechbuild.com/api/targetConfig/config/v2/queryAlertForwardConfigByDeviceId -e redisMinIdle=0 -e iotcoreEndpoint=iotcore-mqtts-stg.ttkdatatechbuild.com -e redisMaxIdle=7000 -e redisHost=r-uf63x4g5p6ir5xao87pd.redis.rds.aliyuncs.com -e jdbcPassword=Youqu48bnb1 -e roid2Url="/api/public/v1/targets/{targetId}/monitoring-status" -e env=dev -e mqttNormalTopic=kinesis-to-lambda-stg -e redisPassword=B2BGn4gK4htgkEwP -e redisDatabase=0 -e redisMaxWait=10000 -e jdbcUrl=jdbc:mysql://rm-bp11k2zm2fr7864428o.mysql.rds.aliyuncs.com/data_center_aeon_admin -e roidAlarmCancelUrl="/api/public/v1/problem-reports/return-to-normal" -e redisPort=6379 -e roidAlarmUrl="/api/public/v1/problem-reports/alarm" -e roid2Authorization="Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..." -e roidBaStatusUrl="/api/public/v1/targets/{targetId}/running-status" 923770123186.dkr.ecr.ap-northeast-1.amazonaws.com/tokyo-build-sender \ No newline at end of file diff --git a/deploy.bat b/deploy.bat new file mode 100644 index 0000000..5f540fc --- /dev/null +++ b/deploy.bat @@ -0,0 +1,2 @@ +scp -i ./TECHSOR_OFFICIAL.pem ./target/TECHSOR_dataCenter_sender-0.0.1-SNAPSHOT.jar root@8.209.255.206:~ +ssh -i ./TECHSOR_OFFICIAL.pem root@8.209.255.206 \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..a16b543 --- /dev/null +++ b/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..c8d4337 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4da5b5f --- /dev/null +++ b/pom.xml @@ -0,0 +1,533 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.12 + + + com.techsor + TECHSOR_dataCenter_sender + 0.0.1-SNAPSHOT + TECHSOR_dataCenter_sender + Demo project for Spring Boot + + 17 + 381659385655.dkr.ecr.ap-northeast-1.amazonaws.com + 923770123186.dkr.ecr.ap-northeast-1.amazonaws.com + tokyo-build-sender + + + + + software.amazon.awssdk + bom + 2.20.113 + pom + import + + + software.amazon.awssdk + services + 2.20.113 + + + software.amazon.kinesis + amazon-kinesis-client-pom + 3.1.0 + + + io.netty + netty-common + 4.2.2.Final + + + io.netty + netty-buffer + 4.2.2.Final + + + io.netty + netty-resolver + 4.2.2.Final + + + io.netty + netty-transport + 4.2.2.Final + + + io.netty + netty-transport-native-unix-common + 4.2.2.Final + + + io.netty + netty-codec-base + 4.2.2.Final + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + org.apache.tomcat.embed + tomcat-embed-core + + + + + + org.apache.tomcat.embed + tomcat-embed-core + 10.1.42 + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-data-redis + 2.5.4 + + + org.springframework.boot + spring-boot-starter-integration + + + + org.springframework.integration + spring-integration-redis + + + org.springframework.boot + spring-boot-starter-actuator + + + + com.lmax + disruptor + 3.3.6 + + + com.mysql + mysql-connector-j + 9.3.0 + + + org.projectlombok + lombok + true + + + + com.google.code.gson + gson + 2.13.1 + + + org.springframework.boot + spring-boot-starter-jdbc + + + + + org.apache.commons + commons-text + 1.13.1 + + + + + com.alibaba.fastjson2 + fastjson2 + 2.0.57 + + + + godzilla + godzilla + 1.6.0 + system + ${project.basedir}/src/libs/godzilla-sdk-1.6.0.jar + + + + org.slf4j + slf4j-api + 2.0.17 + + + org.slf4j + jul-to-slf4j + 1.7.7 + + + org.slf4j + jcl-over-slf4j + 1.7.7 + + + org.slf4j + log4j-over-slf4j + 1.7.7 + + + + ch.qos.logback + logback-classic + 1.5.18 + compile + + + ch.qos.logback + logback-core + 1.5.18 + compile + + + org.java-websocket + Java-WebSocket + 1.5.0 + + + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + + org.apache.commons + commons-pool2 + 2.11.1 + + + org.mockito + mockito-core + 4.11.0 + test + + + cn.hutool + hutool-all + 5.8.38 + + + + com.jayway.jsonpath + json-path + 2.9.0 + + + + + com.amazonaws + aws-java-sdk-dynamodb + 1.12.701 + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.mvel + mvel2 + 2.4.12.Final + + + org.junit.vintage + junit-vintage-engine + 5.8.2 + test + + + net.bytebuddy + byte-buddy + 1.8.16 + + + net.bytebuddy + byte-buddy-agent + 1.8.16 + test + + + + com.google.guava + guava + 33.4.8-jre + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.5 + + + + org.bouncycastle + bcprov-jdk18on + 1.81 + + + + commons-io + commons-io + 2.19.0 + + + + + + + + + + + + javax.mail + mail + 1.4.7 + + + + com.amazonaws + amazon-kinesis-producer + 0.15.10 + + + org.apache.kafka + kafka-clients + + + + + + org.apache.kafka + kafka-clients + 4.0.0 + + + software.amazon.awssdk + kinesis + + + + + software.amazon.kinesis + amazon-kinesis-client + 3.0.3 + + + com.amazonaws + aws-java-sdk-kinesis + 1.12.681 + + + + com.amazonaws + aws-java-sdk-core + 1.12.681 + + + + + org.yaml + snakeyaml + 2.4 + + + + + org.json + json + 20250517 + + + + + net.bytebuddy + byte-buddy + 1.17.5 + + + + + org.apache.logging.log4j + log4j-core + 2.24.3 + + + org.apache.logging.log4j + log4j-api + 2.24.3 + + + + + net.minidev + json-smart + 2.5.2 + + + + + com.google.protobuf + protobuf-java + 4.31.1 + + + + + org.apache.commons + commons-compress + 1.27.1 + + + + + com.squareup.wire + wire-compiler + 5.2.1 + runtime + + + + + com.squareup.wire + wire-schema + 5.2.1 + + + + + org.apache.avro + avro + 1.12.0 + + + + + io.netty + netty-handler + 4.2.2.Final + + + + + org.springframework + spring-context + 6.1.21 + + + + + org.springframework + spring-context-support + 6.1.21 + + + + + com.github.seratch + jslack + 3.4.2 + + + + + + data-center-sender + + + org.springframework.boot + spring-boot-maven-plugin + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + + revision + + + + + true + yyyy-MM-dd'T'HH:mm:ssZ + true + ${project.build.outputDirectory}/git.properties + + + + + + + + + + + + + + + + + + + + + + + + + + + io.fabric8 + docker-maven-plugin + 0.38.1 + + + AKIA5OFH5OOZPCXZIRUQ + TMIN27+OxamT1FmBQSVKfUIWpOVldhxQx2Stxwix + + + + ${aws.ecr.registryTest}/${aws.ecr.repository}:latest + ${aws.ecr.registry} + + ${project.basedir}/Dockerfile + + + + + + + + + + + + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..9bf2f5b --- /dev/null +++ b/readme.md @@ -0,0 +1,57 @@ +# Techsor数据转发系统接收部分 +# Version V0.2.1 + +# DBM +mqtt订阅已经完成 + +# OviPhone +测试接口: +http://IP:8200/api/v1/oviphone/raw +功能:接收数据并存储进restful_history. + +# RoadMap + ++ 代码优化 ++ 功能实现 ++ 测试代码 + + +### docker 环境配置 +#### 环境启动问题 +aws上应该可以设置环境变量 `env` + +`env`=dev + +或者 +`env`=prd + +#### 接收服务器 + + + + + +## ???? +```properties +spring.datasource.url=${jdbcUrl} //jdbcUrl +spring.datasource.username=${jdbcUsername} //jdbcUsername +spring.datasource.password=${jdbcPassword} //jdbcPassword +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.hikari.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.hikari.schema=data_center + +spring.redis.host=${redisHost} +spring.redis.password=${redisPassword} +spring.redis.port=${redisPort} +spring.redis.database=${redisDatabase} +spring.redis.timeout=${redisTimeout} +spring.redis.lettuce.pool.max-active=${redisMaxActive} +spring.redis.lettuce.pool.min-idle=${redisMinIdle} +spring.redis.lettuce.pool.max-idle=${redisMaxIdle} +spring.redis.lettuce.pool.max-wait=${redisMaxWait} +spring.redis.lettuce.shutdown-timeout=${redisShutdownTimeout} + + +data.center.sender.url=${dataCenterSenderTargetUrl} +``` + diff --git a/shell-build.sh b/shell-build.sh new file mode 100644 index 0000000..4e46985 --- /dev/null +++ b/shell-build.sh @@ -0,0 +1,3 @@ +git pull +mvn clean +mvn package -DskipTests=true docker:build diff --git a/src/libs/godzilla-sdk-1.6.0.jar b/src/libs/godzilla-sdk-1.6.0.jar new file mode 100644 index 0000000..2fdc73f Binary files /dev/null and b/src/libs/godzilla-sdk-1.6.0.jar differ diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile new file mode 100644 index 0000000..a9fb694 --- /dev/null +++ b/src/main/docker/Dockerfile @@ -0,0 +1,11 @@ +FROM openjdk:8u131-jdk +WORKDIR /app +COPY data-center-sender.jar app.jar +EXPOSE 8201 + + +# 使用UseCGroupMemoryLimitForHeap +ENV JAVA_OPTS="-Xms2g -Xmx2g -XX:+UseParallelGC -XX:ParallelGCThreads=2 -XX:MaxGCPauseMillis=200 -XX:GCTimeRatio=19 -XX:NewRatio=3 -XX:+AlwaysPreTouch -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/app/gc.log" + +# 使用shell方式的ENTRYPOINT来确保环境变量被展开 +ENTRYPOINT java $JAVA_OPTS -jar app.jar --spring-profiles=$env \ No newline at end of file diff --git a/src/main/java/com/baidu/fsg/uid/BitsAllocator.java b/src/main/java/com/baidu/fsg/uid/BitsAllocator.java new file mode 100644 index 0000000..dc4327c --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/BitsAllocator.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.springframework.util.Assert; + +/** + * Allocate 64 bits for the UID(long)
+ * sign (fixed 1bit) -> deltaSecond -> workerId -> sequence(within the same second) + * + * @author yutianbao + */ +public class BitsAllocator { + /** + * Total 64 bits + */ + public static final int TOTAL_BITS = 1 << 6; + + /** + * Bits for [sign-> second-> workId-> sequence] + */ + private int signBits = 1; + private final int timestampBits; + private final int workerIdBits; + private final int sequenceBits; + + /** + * Max value for workId & sequence + */ + private final long maxDeltaSeconds; + private final long maxWorkerId; + private final long maxSequence; + + /** + * Shift for timestamp & workerId + */ + private final int timestampShift; + private final int workerIdShift; + + /** + * Constructor with timestampBits, workerIdBits, sequenceBits
+ * The highest bit used for sign, so 63 bits for timestampBits, workerIdBits, sequenceBits + */ + public BitsAllocator(int timestampBits, int workerIdBits, int sequenceBits) { + // make sure allocated 64 bits + int allocateTotalBits = signBits + timestampBits + workerIdBits + sequenceBits; + Assert.isTrue(allocateTotalBits == TOTAL_BITS, "allocate not enough 64 bits"); + + // initialize bits + this.timestampBits = timestampBits; + this.workerIdBits = workerIdBits; + this.sequenceBits = sequenceBits; + + // initialize max value + this.maxDeltaSeconds = ~(-1L << timestampBits); + this.maxWorkerId = ~(-1L << workerIdBits); + this.maxSequence = ~(-1L << sequenceBits); + + // initialize shift + this.timestampShift = workerIdBits + sequenceBits; + this.workerIdShift = sequenceBits; + } + + /** + * Allocate bits for UID according to delta seconds & workerId & sequence
+ * Note that: The highest bit will always be 0 for sign + * + * @param deltaSeconds + * @param workerId + * @param sequence + * @return + */ + public long allocate(long deltaSeconds, long workerId, long sequence) { + return (deltaSeconds << timestampShift) | (workerId << workerIdShift) | sequence; + } + + /** + * Getters + */ + public int getSignBits() { + return signBits; + } + + public int getTimestampBits() { + return timestampBits; + } + + public int getWorkerIdBits() { + return workerIdBits; + } + + public int getSequenceBits() { + return sequenceBits; + } + + public long getMaxDeltaSeconds() { + return maxDeltaSeconds; + } + + public long getMaxWorkerId() { + return maxWorkerId; + } + + public long getMaxSequence() { + return maxSequence; + } + + public int getTimestampShift() { + return timestampShift; + } + + public int getWorkerIdShift() { + return workerIdShift; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } + +} \ No newline at end of file diff --git a/src/main/java/com/baidu/fsg/uid/UidGenerator.java b/src/main/java/com/baidu/fsg/uid/UidGenerator.java new file mode 100644 index 0000000..a0fe2ea --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/UidGenerator.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid; + + +import com.baidu.fsg.uid.exception.UidGenerateException; + +/** + * Represents a unique id generator. + * + * @author yutianbao + */ +public interface UidGenerator { + + /** + * Get a unique ID + * + * @return UID + * @throws UidGenerateException + */ + long getUID() throws UidGenerateException; + + /** + * Parse the UID into elements which are used to generate the UID.
+ * Such as timestamp & workerId & sequence... + * + * @param uid + * @return Parsed info + */ + String parseUID(long uid); + +} diff --git a/src/main/java/com/baidu/fsg/uid/buffer/BufferPaddingExecutor.java b/src/main/java/com/baidu/fsg/uid/buffer/BufferPaddingExecutor.java new file mode 100644 index 0000000..8632285 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/buffer/BufferPaddingExecutor.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.buffer; + + +import com.baidu.fsg.uid.utils.NamingThreadFactory; +import com.baidu.fsg.uid.utils.PaddedAtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Represents an executor for padding {@link RingBuffer}
+ * There are two kinds of executors: one for scheduled padding, the other for padding immediately. + * + * @author yutianbao + */ +public class BufferPaddingExecutor { + private static final Logger LOGGER = LoggerFactory.getLogger(RingBuffer.class); + + /** Constants */ + private static final String WORKER_NAME = "RingBuffer-Padding-Worker"; + private static final String SCHEDULE_NAME = "RingBuffer-Padding-Schedule"; + private static final long DEFAULT_SCHEDULE_INTERVAL = 5 * 60L; // 5 minutes + + /** Whether buffer padding is running */ + private final AtomicBoolean running; + + /** We can borrow UIDs from the future, here store the last second we have consumed */ + private final PaddedAtomicLong lastSecond; + + /** RingBuffer & BufferUidProvider */ + private final RingBuffer ringBuffer; + private final BufferedUidProvider uidProvider; + + /** Padding immediately by the thread pool */ + private final ExecutorService bufferPadExecutors; + /** Padding schedule thread */ + private final ScheduledExecutorService bufferPadSchedule; + + /** Schedule interval Unit as seconds */ + private long scheduleInterval = DEFAULT_SCHEDULE_INTERVAL; + + /** + * Constructor with {@link RingBuffer} and {@link BufferedUidProvider}, default use schedule + * + * @param ringBuffer {@link RingBuffer} + * @param uidProvider {@link BufferedUidProvider} + */ + public BufferPaddingExecutor(RingBuffer ringBuffer, BufferedUidProvider uidProvider) { + this(ringBuffer, uidProvider, true); + } + + /** + * Constructor with {@link RingBuffer}, {@link BufferedUidProvider}, and whether use schedule padding + * + * @param ringBuffer {@link RingBuffer} + * @param uidProvider {@link BufferedUidProvider} + * @param usingSchedule + */ + public BufferPaddingExecutor(RingBuffer ringBuffer, BufferedUidProvider uidProvider, boolean usingSchedule) { + this.running = new AtomicBoolean(false); + this.lastSecond = new PaddedAtomicLong(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())); + this.ringBuffer = ringBuffer; + this.uidProvider = uidProvider; + + // initialize thread pool + int cores = Runtime.getRuntime().availableProcessors(); + bufferPadExecutors = Executors.newFixedThreadPool(cores * 2, new NamingThreadFactory(WORKER_NAME)); + + // initialize schedule thread + if (usingSchedule) { + bufferPadSchedule = Executors.newSingleThreadScheduledExecutor(new NamingThreadFactory(SCHEDULE_NAME)); + } else { + bufferPadSchedule = null; + } + } + + /** + * Start executors such as schedule + */ + public void start() { + if (bufferPadSchedule != null) { + bufferPadSchedule.scheduleWithFixedDelay(() -> paddingBuffer(), scheduleInterval, scheduleInterval, TimeUnit.SECONDS); + } + } + + /** + * Shutdown executors + */ + public void shutdown() { + if (!bufferPadExecutors.isShutdown()) { + bufferPadExecutors.shutdownNow(); + } + + if (bufferPadSchedule != null && !bufferPadSchedule.isShutdown()) { + bufferPadSchedule.shutdownNow(); + } + } + + /** + * Whether is padding + * + * @return + */ + public boolean isRunning() { + return running.get(); + } + + /** + * Padding buffer in the thread pool + */ + public void asyncPadding() { + bufferPadExecutors.submit(this::paddingBuffer); + } + + /** + * Padding buffer fill the slots until to catch the cursor + */ + public void paddingBuffer() { + LOGGER.info("Ready to padding buffer lastSecond:{}. {}", lastSecond.get(), ringBuffer); + + // is still running + if (!running.compareAndSet(false, true)) { + LOGGER.info("Padding buffer is still running. {}", ringBuffer); + return; + } + + // fill the rest slots until to catch the cursor + boolean isFullRingBuffer = false; + while (!isFullRingBuffer) { + List uidList = uidProvider.provide(lastSecond.incrementAndGet()); + for (Long uid : uidList) { + isFullRingBuffer = !ringBuffer.put(uid); + if (isFullRingBuffer) { + break; + } + } + } + + // not running now + running.compareAndSet(true, false); + LOGGER.info("End to padding buffer lastSecond:{}. {}", lastSecond.get(), ringBuffer); + } + + /** + * Setters + */ + public void setScheduleInterval(long scheduleInterval) { + Assert.isTrue(scheduleInterval > 0, "Schedule interval must positive!"); + this.scheduleInterval = scheduleInterval; + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/buffer/BufferedUidProvider.java b/src/main/java/com/baidu/fsg/uid/buffer/BufferedUidProvider.java new file mode 100644 index 0000000..6b332cf --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/buffer/BufferedUidProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.buffer; + +import java.util.List; + +/** + * Buffered UID provider(Lambda supported), which provides UID in the same one second + * + * @author yutianbao + */ +@FunctionalInterface +public interface BufferedUidProvider { + + /** + * Provides UID in one second + * + * @param momentInSecond + * @return + */ + List provide(long momentInSecond); +} diff --git a/src/main/java/com/baidu/fsg/uid/buffer/RejectedPutBufferHandler.java b/src/main/java/com/baidu/fsg/uid/buffer/RejectedPutBufferHandler.java new file mode 100644 index 0000000..c9f9d34 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/buffer/RejectedPutBufferHandler.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.buffer; + +/** + * If tail catches the cursor it means that the ring buffer is full, any more buffer put request will be rejected. + * Specify the policy to handle the reject. This is a Lambda supported interface + * + * @author yutianbao + */ +@FunctionalInterface +public interface RejectedPutBufferHandler { + + /** + * Reject put buffer request + * + * @param ringBuffer + * @param uid + */ + void rejectPutBuffer(RingBuffer ringBuffer, long uid); +} diff --git a/src/main/java/com/baidu/fsg/uid/buffer/RejectedTakeBufferHandler.java b/src/main/java/com/baidu/fsg/uid/buffer/RejectedTakeBufferHandler.java new file mode 100644 index 0000000..0afe679 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/buffer/RejectedTakeBufferHandler.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.buffer; + +/** + * If cursor catches the tail it means that the ring buffer is empty, any more buffer take request will be rejected. + * Specify the policy to handle the reject. This is a Lambda supported interface + * + * @author yutianbao + */ +@FunctionalInterface +public interface RejectedTakeBufferHandler { + + /** + * Reject take buffer request + * + * @param ringBuffer + */ + void rejectTakeBuffer(RingBuffer ringBuffer); +} diff --git a/src/main/java/com/baidu/fsg/uid/buffer/RingBuffer.java b/src/main/java/com/baidu/fsg/uid/buffer/RingBuffer.java new file mode 100644 index 0000000..4e46433 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/buffer/RingBuffer.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.buffer; + + +import com.baidu.fsg.uid.utils.PaddedAtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Represents a ring buffer based on array.
+ * Using array could improve read element performance due to the CUP cache line. To prevent + * the side effect of False Sharing, {@link PaddedAtomicLong} is using on 'tail' and 'cursor'

+ * + * A ring buffer is consisted of: + *

  • slots: each element of the array is a slot, which is be set with a UID + *
  • flags: flag array corresponding the same index with the slots, indicates whether can take or put slot + *
  • tail: a sequence of the max slot position to produce + *
  • cursor: a sequence of the min slot position to consume + * + * @author yutianbao + */ +public class RingBuffer { + private static final Logger LOGGER = LoggerFactory.getLogger(RingBuffer.class); + + /** Constants */ + private static final int START_POINT = -1; + private static final long CAN_PUT_FLAG = 0L; + private static final long CAN_TAKE_FLAG = 1L; + public static final int DEFAULT_PADDING_PERCENT = 50; + + /** The size of RingBuffer's slots, each slot hold a UID */ + private final int bufferSize; + private final long indexMask; + private final long[] slots; + private final PaddedAtomicLong[] flags; + + /** Tail: last position sequence to produce */ + private final AtomicLong tail = new PaddedAtomicLong(START_POINT); + + /** Cursor: current position sequence to consume */ + private final AtomicLong cursor = new PaddedAtomicLong(START_POINT); + + /** Threshold for trigger padding buffer*/ + private final int paddingThreshold; + + /** Reject put/take buffer handle policy */ + private RejectedPutBufferHandler rejectedPutHandler = this::discardPutBuffer; + private RejectedTakeBufferHandler rejectedTakeHandler = this::exceptionRejectedTakeBuffer; + + /** Executor of padding buffer */ + private BufferPaddingExecutor bufferPaddingExecutor; + + /** + * Constructor with buffer size, paddingFactor default as {@value #DEFAULT_PADDING_PERCENT} + * + * @param bufferSize must be positive & a power of 2 + */ + public RingBuffer(int bufferSize) { + this(bufferSize, DEFAULT_PADDING_PERCENT); + } + + /** + * Constructor with buffer size & padding factor + * + * @param bufferSize must be positive & a power of 2 + * @param paddingFactor percent in (0 - 100). When the count of rest available UIDs reach the threshold, it will trigger padding buffer
    + * Sample: paddingFactor=20, bufferSize=1000 -> threshold=1000 * 20 /100, + * padding buffer will be triggered when tail-cursor 0L, "RingBuffer size must be positive"); + Assert.isTrue(Integer.bitCount(bufferSize) == 1, "RingBuffer size must be a power of 2"); + Assert.isTrue(paddingFactor > 0 && paddingFactor < 100, "RingBuffer size must be positive"); + + this.bufferSize = bufferSize; + this.indexMask = bufferSize - 1; + this.slots = new long[bufferSize]; + this.flags = initFlags(bufferSize); + + this.paddingThreshold = bufferSize * paddingFactor / 100; + } + + /** + * Put an UID in the ring & tail moved
    + * We use 'synchronized' to guarantee the UID fill in slot & publish new tail sequence as atomic operations
    + * + * Note that: It is recommended to put UID in a serialize way, cause we once batch generate a series UIDs and put + * the one by one into the buffer, so it is unnecessary put in multi-threads + * + * @param uid + * @return false means that the buffer is full, apply {@link RejectedPutBufferHandler} + */ + public synchronized boolean put(long uid) { + long currentTail = tail.get(); + long currentCursor = cursor.get(); + + // tail catches the cursor, means that you can't put any cause of RingBuffer is full + long distance = currentTail - (currentCursor == START_POINT ? 0 : currentCursor); + if (distance == bufferSize - 1) { + rejectedPutHandler.rejectPutBuffer(this, uid); + return false; + } + + // 1. pre-check whether the flag is CAN_PUT_FLAG + int nextTailIndex = calSlotIndex(currentTail + 1); + if (flags[nextTailIndex].get() != CAN_PUT_FLAG) { + rejectedPutHandler.rejectPutBuffer(this, uid); + return false; + } + + // 2. put UID in the next slot + // 3. update next slot' flag to CAN_TAKE_FLAG + // 4. publish tail with sequence increase by one + slots[nextTailIndex] = uid; + flags[nextTailIndex].set(CAN_TAKE_FLAG); + tail.incrementAndGet(); + + // The atomicity of operations above, guarantees by 'synchronized'. In another word, + // the take operation can't consume the UID we just put, until the tail is published(tail.incrementAndGet()) + return true; + } + + /** + * Take an UID of the ring at the next cursor, this is a lock free operation by using atomic cursor

    + * + * Before getting the UID, we also check whether reach the padding threshold, + * the padding buffer operation will be triggered in another thread
    + * If there is no more available UID to be taken, the specified {@link RejectedTakeBufferHandler} will be applied
    + * + * @return UID + * @throws IllegalStateException if the cursor moved back + */ + public long take() { + // spin get next available cursor + long currentCursor = cursor.get(); + long nextCursor = cursor.updateAndGet(old -> old == tail.get() ? old : old + 1); + + // check for safety consideration, it never occurs + Assert.isTrue(nextCursor >= currentCursor, "Curosr can't move back"); + + // trigger padding in an async-mode if reach the threshold + long currentTail = tail.get(); + if (currentTail - nextCursor < paddingThreshold) { + LOGGER.info("Reach the padding threshold:{}. tail:{}, cursor:{}, rest:{}", paddingThreshold, currentTail, + nextCursor, currentTail - nextCursor); + bufferPaddingExecutor.asyncPadding(); + } + + // cursor catch the tail, means that there is no more available UID to take + if (nextCursor == currentCursor) { + rejectedTakeHandler.rejectTakeBuffer(this); + } + + // 1. check next slot flag is CAN_TAKE_FLAG + int nextCursorIndex = calSlotIndex(nextCursor); + Assert.isTrue(flags[nextCursorIndex].get() == CAN_TAKE_FLAG, "Curosr not in can take status"); + + // 2. get UID from next slot + // 3. set next slot flag as CAN_PUT_FLAG. + long uid = slots[nextCursorIndex]; + flags[nextCursorIndex].set(CAN_PUT_FLAG); + + // Note that: Step 2,3 can not swap. If we set flag before get value of slot, the producer may overwrite the + // slot with a new UID, and this may cause the consumer take the UID twice after walk a round the ring + return uid; + } + + /** + * Calculate slot index with the slot sequence (sequence % bufferSize) + */ + protected int calSlotIndex(long sequence) { + return (int) (sequence & indexMask); + } + + /** + * Discard policy for {@link RejectedPutBufferHandler}, we just do logging + */ + protected void discardPutBuffer(RingBuffer ringBuffer, long uid) { + LOGGER.warn("Rejected putting buffer for uid:{}. {}", uid, ringBuffer); + } + + /** + * Policy for {@link RejectedTakeBufferHandler}, throws {@link RuntimeException} after logging + */ + protected void exceptionRejectedTakeBuffer(RingBuffer ringBuffer) { + LOGGER.warn("Rejected take buffer. {}", ringBuffer); + throw new RuntimeException("Rejected take buffer. " + ringBuffer); + } + + /** + * Initialize flags as CAN_PUT_FLAG + */ + private PaddedAtomicLong[] initFlags(int bufferSize) { + PaddedAtomicLong[] flags = new PaddedAtomicLong[bufferSize]; + for (int i = 0; i < bufferSize; i++) { + flags[i] = new PaddedAtomicLong(CAN_PUT_FLAG); + } + + return flags; + } + + /** + * Getters + */ + public long getTail() { + return tail.get(); + } + + public long getCursor() { + return cursor.get(); + } + + public int getBufferSize() { + return bufferSize; + } + + /** + * Setters + */ + public void setBufferPaddingExecutor(BufferPaddingExecutor bufferPaddingExecutor) { + this.bufferPaddingExecutor = bufferPaddingExecutor; + } + + public void setRejectedPutHandler(RejectedPutBufferHandler rejectedPutHandler) { + this.rejectedPutHandler = rejectedPutHandler; + } + + public void setRejectedTakeHandler(RejectedTakeBufferHandler rejectedTakeHandler) { + this.rejectedTakeHandler = rejectedTakeHandler; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("RingBuffer [bufferSize=").append(bufferSize) + .append(", tail=").append(tail) + .append(", cursor=").append(cursor) + .append(", paddingThreshold=").append(paddingThreshold).append("]"); + + return builder.toString(); + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/exception/UidGenerateException.java b/src/main/java/com/baidu/fsg/uid/exception/UidGenerateException.java new file mode 100644 index 0000000..ae7aaff --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/exception/UidGenerateException.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.exception; + +/** + * UidGenerateException + * + * @author yutianbao + */ +public class UidGenerateException extends RuntimeException { + + /** + * Serial Version UID + */ + private static final long serialVersionUID = -27048199131316992L; + + /** + * Default constructor + */ + public UidGenerateException() { + super(); + } + + /** + * Constructor with message & cause + * + * @param message + * @param cause + */ + public UidGenerateException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor with message + * + * @param message + */ + public UidGenerateException(String message) { + super(message); + } + + /** + * Constructor with message format + * + * @param msgFormat + * @param args + */ + public UidGenerateException(String msgFormat, Object... args) { + super(String.format(msgFormat, args)); + } + + /** + * Constructor with cause + * + * @param cause + */ + public UidGenerateException(Throwable cause) { + super(cause); + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/impl/CachedUidGenerator.java b/src/main/java/com/baidu/fsg/uid/impl/CachedUidGenerator.java new file mode 100644 index 0000000..2364e4c --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/impl/CachedUidGenerator.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.impl; + + +import com.baidu.fsg.uid.buffer.BufferPaddingExecutor; +import com.baidu.fsg.uid.buffer.RejectedPutBufferHandler; +import com.baidu.fsg.uid.buffer.RejectedTakeBufferHandler; +import com.baidu.fsg.uid.buffer.RingBuffer; +import com.baidu.fsg.uid.exception.UidGenerateException; +import com.baidu.fsg.uid.UidGenerator; +import com.baidu.fsg.uid.BitsAllocator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a cached implementation of {@link UidGenerator} extends + * from {@link DefaultUidGenerator}, based on a lock free {@link RingBuffer}

    + * + * The spring properties you can specified as below:
    + *

  • boostPower: RingBuffer size boost for a power of 2, Sample: boostPower is 3, it means the buffer size + * will be ({@link BitsAllocator#getMaxSequence()} + 1) << + * {@link #boostPower}, Default as {@value #DEFAULT_BOOST_POWER} + *
  • paddingFactor: Represents a percent value of (0 - 100). When the count of rest available UIDs reach the + * threshold, it will trigger padding buffer. Default as{@link RingBuffer#DEFAULT_PADDING_PERCENT} + * Sample: paddingFactor=20, bufferSize=1000 -> threshold=1000 * 20 /100, padding buffer will be triggered when tail-cursorscheduleInterval: Padding buffer in a schedule, specify padding buffer interval, Unit as second + *
  • rejectedPutBufferHandler: Policy for rejected put buffer. Default as discard put request, just do logging + *
  • rejectedTakeBufferHandler: Policy for rejected take buffer. Default as throwing up an exception + * + * @author yutianbao + */ +public class CachedUidGenerator extends DefaultUidGenerator implements DisposableBean { + private static final Logger LOGGER = LoggerFactory.getLogger(CachedUidGenerator.class); + private static final int DEFAULT_BOOST_POWER = 3; + + /** Spring properties */ + private int boostPower = DEFAULT_BOOST_POWER; + private int paddingFactor = RingBuffer.DEFAULT_PADDING_PERCENT; + private Long scheduleInterval; + + private RejectedPutBufferHandler rejectedPutBufferHandler; + private RejectedTakeBufferHandler rejectedTakeBufferHandler; + + /** RingBuffer */ + private RingBuffer ringBuffer; + private BufferPaddingExecutor bufferPaddingExecutor; + + @Override + public void afterPropertiesSet() throws Exception { + // initialize workerId & bitsAllocator + super.afterPropertiesSet(); + + // initialize RingBuffer & RingBufferPaddingExecutor + this.initRingBuffer(); + LOGGER.info("Initialized RingBuffer successfully."); + } + + @Override + public long getUID() { + try { + System.out.println("我是cache的uid"); + return ringBuffer.take(); + } catch (Exception e) { + LOGGER.error("Generate unique id exception. ", e); + throw new UidGenerateException(e); + } + } + + @Override + public String parseUID(long uid) { + return super.parseUID(uid); + } + + @Override + public void destroy() throws Exception { + bufferPaddingExecutor.shutdown(); + } + + /** + * Get the UIDs in the same specified second under the max sequence + * + * @param currentSecond + * @return UID list, size of {@link BitsAllocator#getMaxSequence()} + 1 + */ + protected List nextIdsForOneSecond(long currentSecond) { + // Initialize result list size of (max sequence + 1) + int listSize = (int) bitsAllocator.getMaxSequence() + 1; + List uidList = new ArrayList<>(listSize); + + // Allocate the first sequence of the second, the others can be calculated with the offset + long firstSeqUid = bitsAllocator.allocate(currentSecond - epochSeconds, workerId, 0L); + for (int offset = 0; offset < listSize; offset++) { + uidList.add(firstSeqUid + offset); + } + + return uidList; + } + + /** + * Initialize RingBuffer & RingBufferPaddingExecutor + */ + private void initRingBuffer() { + // initialize RingBuffer + int bufferSize = ((int) bitsAllocator.getMaxSequence() + 1) << boostPower; + this.ringBuffer = new RingBuffer(bufferSize, paddingFactor); + LOGGER.info("Initialized ring buffer size:{}, paddingFactor:{}", bufferSize, paddingFactor); + + // initialize RingBufferPaddingExecutor + boolean usingSchedule = (scheduleInterval != null); + this.bufferPaddingExecutor = new BufferPaddingExecutor(ringBuffer, this::nextIdsForOneSecond, usingSchedule); + if (usingSchedule) { + bufferPaddingExecutor.setScheduleInterval(scheduleInterval); + } + + LOGGER.info("Initialized BufferPaddingExecutor. Using schdule:{}, interval:{}", usingSchedule, scheduleInterval); + + // set rejected put/take handle policy + this.ringBuffer.setBufferPaddingExecutor(bufferPaddingExecutor); + if (rejectedPutBufferHandler != null) { + this.ringBuffer.setRejectedPutHandler(rejectedPutBufferHandler); + } + if (rejectedTakeBufferHandler != null) { + this.ringBuffer.setRejectedTakeHandler(rejectedTakeBufferHandler); + } + + // fill in all slots of the RingBuffer + bufferPaddingExecutor.paddingBuffer(); + + // start buffer padding threads + bufferPaddingExecutor.start(); + } + + /** + * Setters for spring property + */ + public void setBoostPower(int boostPower) { + Assert.isTrue(boostPower > 0, "Boost power must be positive!"); + this.boostPower = boostPower; + } + + public void setRejectedPutBufferHandler(RejectedPutBufferHandler rejectedPutBufferHandler) { + Assert.notNull(rejectedPutBufferHandler, "RejectedPutBufferHandler can't be null!"); + this.rejectedPutBufferHandler = rejectedPutBufferHandler; + } + + public void setRejectedTakeBufferHandler(RejectedTakeBufferHandler rejectedTakeBufferHandler) { + Assert.notNull(rejectedTakeBufferHandler, "RejectedTakeBufferHandler can't be null!"); + this.rejectedTakeBufferHandler = rejectedTakeBufferHandler; + } + + public void setScheduleInterval(long scheduleInterval) { + Assert.isTrue(scheduleInterval > 0, "Schedule interval must positive!"); + this.scheduleInterval = scheduleInterval; + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/impl/DefaultUidGenerator.java b/src/main/java/com/baidu/fsg/uid/impl/DefaultUidGenerator.java new file mode 100644 index 0000000..77cd117 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/impl/DefaultUidGenerator.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.impl; + +import com.baidu.fsg.uid.exception.UidGenerateException; +import com.baidu.fsg.uid.UidGenerator; +import com.baidu.fsg.uid.BitsAllocator; +import com.baidu.fsg.uid.utils.DateUtils; +import com.baidu.fsg.uid.worker.WorkerIdAssigner; + +import com.techsor.datacenter.sender.config.UidProperties; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +/** + * Represents an implementation of {@link UidGenerator} + * + * The unique id has 64bits (long), default allocated as blow:
    + *
  • sign: The highest bit is 0 + *
  • delta seconds: The next 28 bits, represents delta seconds since a customer epoch(2016-05-20 00:00:00.000). + * Supports about 8.7 years until to 2024-11-20 21:24:16 + *
  • worker id: The next 22 bits, represents the worker's id which assigns based on database, max id is about 420W + *
  • sequence: The next 13 bits, represents a sequence within the same second, max for 8192/s

    + * + * The {@link DefaultUidGenerator#parseUID(long)} is a tool method to parse the bits + * + *
    {@code
    + * +------+----------------------+----------------+-----------+
    + * | sign |     delta seconds    | worker node id | sequence  |
    + * +------+----------------------+----------------+-----------+
    + *   1bit          28bits              22bits         13bits
    + * }
    + * + * You can also specified the bits by Spring property setting. + *
  • timeBits: default as 28 + *
  • workerBits: default as 22 + *
  • seqBits: default as 13 + *
  • epochStr: Epoch date string format 'yyyy-MM-dd'. Default as '2016-05-20'

    + * + * Note that: The total bits must be 64 -1 + * + * @author yutianbao + */ +@Component +public class DefaultUidGenerator implements UidGenerator, InitializingBean { + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultUidGenerator.class); + @Autowired + UidProperties uidProperties; + /** Bits allocate */ + /** + * timeBits = 31 68年 + * workerBits = 17 每天重启5次可用70年 + * seqBits = 15 每秒32768个并发 + */ + + + /** Customer epoch, unit as second. For example 2016-05-20 (ms: 1463673600000)*/ + /** + * 默认时间更改为2020-10-28(毫秒值:1603814400000) + */ + protected String epochStr = "2023-11-01"; + protected long epochSeconds = TimeUnit.MILLISECONDS.toSeconds(1698768000000L); + + /** Stable fields after spring bean initializing */ + protected BitsAllocator bitsAllocator; + protected long workerId; + + /** Volatile fields caused by nextId() */ + protected long sequence = 0L; + protected long lastSecond = -1L; + + /** Spring property */ + @Autowired + protected WorkerIdAssigner workerIdAssigner; + + @Override + public void afterPropertiesSet() throws Exception { + // initialize bits allocator + bitsAllocator = new BitsAllocator(uidProperties.getTimeBits(), uidProperties.getWorkerBits(), uidProperties.getSeqBits()); + + // initialize worker id + workerId = workerIdAssigner.assignWorkerId(); + if (workerId > bitsAllocator.getMaxWorkerId()) { + throw new RuntimeException("Worker id " + workerId + " exceeds the max " + bitsAllocator.getMaxWorkerId()); + } + + LOGGER.info("Initialized bits(1, {}, {}, {}) for workerID:{}", uidProperties.getTimeBits(), uidProperties.getWorkerBits(), uidProperties.getSeqBits(), workerId); + } + + @Override + public long getUID() throws UidGenerateException { + try { + return nextId(); + } catch (Exception e) { + LOGGER.error("Generate unique id exception. ", e); + throw new UidGenerateException(e); + } + } + + @Override + public String parseUID(long uid) { + long totalBits = BitsAllocator.TOTAL_BITS; + long signBits = bitsAllocator.getSignBits(); + long timestampBits = bitsAllocator.getTimestampBits(); + long workerIdBits = bitsAllocator.getWorkerIdBits(); + long sequenceBits = bitsAllocator.getSequenceBits(); + + // parse UID + long sequence = (uid << (totalBits - sequenceBits)) >>> (totalBits - sequenceBits); + long workerId = (uid << (timestampBits + signBits)) >>> (totalBits - workerIdBits); + long deltaSeconds = uid >>> (workerIdBits + sequenceBits); + + Date thatTime = new Date(TimeUnit.SECONDS.toMillis(epochSeconds + deltaSeconds)); + String thatTimeStr = DateUtils.formatByDateTimePattern(thatTime); + + // format as string + return String.format("{\"UID\":\"%d\",\"timestamp\":\"%s\",\"workerId\":\"%d\",\"sequence\":\"%d\"}", + uid, thatTimeStr, workerId, sequence); + } + + /** + * Get UID + * + * @return UID + * @throws UidGenerateException in the case: Clock moved backwards; Exceeds the max timestamp + */ + protected synchronized long nextId() { + long currentSecond = getCurrentSecond(); + + // Clock moved backwards, refuse to generate uid + if (currentSecond < lastSecond) { + long refusedSeconds = lastSecond - currentSecond; + throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds); + } + + // At the same second, increase sequence + if (currentSecond == lastSecond) { + sequence = (sequence + 1) & bitsAllocator.getMaxSequence(); + // Exceed the max sequence, we wait the next second to generate uid + if (sequence == 0) { + currentSecond = getNextSecond(lastSecond); + } + + // At the different second, sequence restart from zero + } else { + sequence = 0L; + } + + lastSecond = currentSecond; + + // Allocate bits for UID + return bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence); + } + + /** + * Get next millisecond + */ + private long getNextSecond(long lastTimestamp) { + long timestamp = getCurrentSecond(); + while (timestamp <= lastTimestamp) { + timestamp = getCurrentSecond(); + } + + return timestamp; + } + + /** + * Get current second + */ + private long getCurrentSecond() { + long currentSecond = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); + if (currentSecond - epochSeconds > bitsAllocator.getMaxDeltaSeconds()) { + throw new UidGenerateException("Timestamp bits is exhausted. Refusing UID generate. Now: " + currentSecond); + } + + return currentSecond; + } + + /** + * Setters for spring property + */ + public void setWorkerIdAssigner(WorkerIdAssigner workerIdAssigner) { + this.workerIdAssigner = workerIdAssigner; + } + + + + public void setEpochStr(String epochStr) { + if (StringUtils.isNotBlank(epochStr)) { + this.epochStr = epochStr; + this.epochSeconds = TimeUnit.MILLISECONDS.toSeconds(DateUtils.parseByDayPattern(epochStr).getTime()); + } + } +} diff --git a/src/main/java/com/baidu/fsg/uid/utils/DateUtils.java b/src/main/java/com/baidu/fsg/uid/utils/DateUtils.java new file mode 100644 index 0000000..b8d11b4 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/utils/DateUtils.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.utils; + + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; + +/** + * DateUtils provides date formatting, parsing + * + * @author yutianbao + */ +public abstract class DateUtils extends org.apache.commons.lang3.time.DateUtils { + /** + * Patterns + */ + public static final String DAY_PATTERN = "yyyy-MM-dd"; + public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; + public static final String DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; + + public static final Date DEFAULT_DATE = DateUtils.parseByDayPattern("1970-01-01"); + + /** + * Parse date by 'yyyy-MM-dd' pattern + * + * @param str + * @return + */ + public static Date parseByDayPattern(String str) { + return parseDate(str, DAY_PATTERN); + } + + /** + * Parse date by 'yyyy-MM-dd HH:mm:ss' pattern + * + * @param str + * @return + */ + public static Date parseByDateTimePattern(String str) { + return parseDate(str, DATETIME_PATTERN); + } + + /** + * Parse date without Checked exception + * + * @param str + * @param pattern + * @return + * @throws RuntimeException when ParseException occurred + */ + public static Date parseDate(String str, String pattern) { + try { + return parseDate(str, new String[]{pattern}); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * Format date into string + * + * @param date + * @param pattern + * @return + */ + public static String formatDate(Date date, String pattern) { + return DateFormatUtils.format(date, pattern); + } + + /** + * Format date by 'yyyy-MM-dd' pattern + * + * @param date + * @return + */ + public static String formatByDayPattern(Date date) { + if (date != null) { + return DateFormatUtils.format(date, DAY_PATTERN); + } else { + return null; + } + } + + /** + * Format date by 'yyyy-MM-dd HH:mm:ss' pattern + * + * @param date + * @return + */ + public static String formatByDateTimePattern(Date date) { + return DateFormatUtils.format(date, DATETIME_PATTERN); + } + + /** + * Get current day using format date by 'yyyy-MM-dd HH:mm:ss' pattern + * + * @return + * @author yebo + */ + public static String getCurrentDayByDayPattern() { + Calendar cal = Calendar.getInstance(); + return formatByDayPattern(cal.getTime()); + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/utils/DockerUtils.java b/src/main/java/com/baidu/fsg/uid/utils/DockerUtils.java new file mode 100644 index 0000000..a7950db --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/utils/DockerUtils.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.utils; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * DockerUtils + * + * @author yutianbao + */ +public abstract class DockerUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(DockerUtils.class); + + /** Environment param keys */ + private static final String ENV_KEY_HOST = "JPAAS_HOST"; + private static final String ENV_KEY_PORT = "JPAAS_HTTP_PORT"; + private static final String ENV_KEY_PORT_ORIGINAL = "JPAAS_HOST_PORT_8080"; + + /** Docker host & port */ + private static String DOCKER_HOST = ""; + private static String DOCKER_PORT = ""; + + /** Whether is docker */ + private static boolean IS_DOCKER; + + static { + retrieveFromEnv(); + } + + /** + * Retrieve docker host + * + * @return empty string if not a docker + */ + public static String getDockerHost() { + return DOCKER_HOST; + } + + /** + * Retrieve docker port + * + * @return empty string if not a docker + */ + public static String getDockerPort() { + return DOCKER_PORT; + } + + /** + * Whether a docker + * + * @return + */ + public static boolean isDocker() { + return IS_DOCKER; + } + + /** + * Retrieve host & port from environment + */ + private static void retrieveFromEnv() { + // retrieve host & port from environment + DOCKER_HOST = System.getenv(ENV_KEY_HOST); + DOCKER_PORT = System.getenv(ENV_KEY_PORT); + + // not found from 'JPAAS_HTTP_PORT', then try to find from 'JPAAS_HOST_PORT_8080' + if (StringUtils.isBlank(DOCKER_PORT)) { + DOCKER_PORT = System.getenv(ENV_KEY_PORT_ORIGINAL); + } + + boolean hasEnvHost = StringUtils.isNotBlank(DOCKER_HOST); + boolean hasEnvPort = StringUtils.isNotBlank(DOCKER_PORT); + + // docker can find both host & port from environment + if (hasEnvHost && hasEnvPort) { + IS_DOCKER = true; + + // found nothing means not a docker, maybe an actual machine + } else if (!hasEnvHost && !hasEnvPort) { + IS_DOCKER = false; + + } else { + LOGGER.error("Missing host or port from env for Docker. host:{}, port:{}", DOCKER_HOST, DOCKER_PORT); + throw new RuntimeException( + "Missing host or port from env for Docker. host:" + DOCKER_HOST + ", port:" + DOCKER_PORT); + } + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/utils/EnumUtils.java b/src/main/java/com/baidu/fsg/uid/utils/EnumUtils.java new file mode 100644 index 0000000..3261539 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/utils/EnumUtils.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.utils; + +import org.springframework.util.Assert; + +/** + * EnumUtils provides the operations for {@link ValuedEnum} such as Parse, value of... + * + * @author yutianbao + */ +public abstract class EnumUtils { + + /** + * Parse the bounded value into ValuedEnum + * + * @param clz + * @param value + * @return + */ + public static , V> T parse(Class clz, V value) { + Assert.notNull(clz, "clz can not be null"); + if (value == null) { + return null; + } + + for (T t : clz.getEnumConstants()) { + if (value.equals(t.value())) { + return t; + } + } + return null; + } + + /** + * Null-safe valueOf function + * + * @param + * @param enumType + * @param name + * @return + */ + public static > T valueOf(Class enumType, String name) { + if (name == null) { + return null; + } + + return Enum.valueOf(enumType, name); + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/utils/NamingThreadFactory.java b/src/main/java/com/baidu/fsg/uid/utils/NamingThreadFactory.java new file mode 100644 index 0000000..58263a0 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/utils/NamingThreadFactory.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.utils; + +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Named thread in ThreadFactory. If there is no specified name for thread, it + * will auto detect using the invoker classname instead. + * + * @author yutianbao + */ +public class NamingThreadFactory implements ThreadFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(NamingThreadFactory.class); + + /** + * Thread name pre + */ + private String name; + /** + * Is daemon thread + */ + private boolean daemon; + /** + * UncaughtExceptionHandler + */ + private UncaughtExceptionHandler uncaughtExceptionHandler; + /** + * Sequences for multi thread name prefix + */ + private final ConcurrentHashMap sequences; + + /** + * Constructors + */ + public NamingThreadFactory() { + this(null, false, null); + } + + public NamingThreadFactory(String name) { + this(name, false, null); + } + + public NamingThreadFactory(String name, boolean daemon) { + this(name, daemon, null); + } + + public NamingThreadFactory(String name, boolean daemon, UncaughtExceptionHandler handler) { + this.name = name; + this.daemon = daemon; + this.uncaughtExceptionHandler = handler; + this.sequences = new ConcurrentHashMap(); + } + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(this.daemon); + + // If there is no specified name for thread, it will auto detect using the invoker classname instead. + // Notice that auto detect may cause some performance overhead + String prefix = this.name; + if (StringUtils.isBlank(prefix)) { + prefix = getInvoker(2); + } + thread.setName(prefix + "-" + getSequence(prefix)); + + // no specified uncaughtExceptionHandler, just do logging. + if (this.uncaughtExceptionHandler != null) { + thread.setUncaughtExceptionHandler(this.uncaughtExceptionHandler); + } else { + thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + LOGGER.error("unhandled exception in thread: " + t.getId() + ":" + t.getName(), e); + } + }); + } + + return thread; + } + + /** + * Get the method invoker's class name + * + * @param depth + * @return + */ + private String getInvoker(int depth) { + Exception e = new Exception(); + StackTraceElement[] stes = e.getStackTrace(); + if (stes.length > depth) { + return ClassUtils.getShortClassName(stes[depth].getClassName()); + } + return getClass().getSimpleName(); + } + + /** + * Get sequence for different naming prefix + * + * @param invoker + * @return + */ + private long getSequence(String invoker) { + AtomicLong r = this.sequences.get(invoker); + if (r == null) { + r = new AtomicLong(0); + AtomicLong previous = this.sequences.putIfAbsent(invoker, r); + if (previous != null) { + r = previous; + } + } + + return r.incrementAndGet(); + } + + /** + * Getters & Setters + */ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isDaemon() { + return daemon; + } + + public void setDaemon(boolean daemon) { + this.daemon = daemon; + } + + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + return uncaughtExceptionHandler; + } + + public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) { + this.uncaughtExceptionHandler = handler; + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/utils/NetUtils.java b/src/main/java/com/baidu/fsg/uid/utils/NetUtils.java new file mode 100644 index 0000000..4f0f17f --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/utils/NetUtils.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.utils; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +/** + * NetUtils + * + * @author yutianbao + */ +public abstract class NetUtils { + + /** + * Pre-loaded local address + */ + public static InetAddress localAddress; + + static { + try { + localAddress = getLocalInetAddress(); + } catch (SocketException e) { + throw new RuntimeException("fail to get local ip."); + } + } + + /** + * Retrieve the first validated local ip address(the Public and LAN ip addresses are validated). + * + * @return the local address + * @throws SocketException the socket exception + */ + public static InetAddress getLocalInetAddress() throws SocketException { + // enumerates all network interfaces + Enumeration enu = NetworkInterface.getNetworkInterfaces(); + + while (enu.hasMoreElements()) { + NetworkInterface ni = enu.nextElement(); + if (ni.isLoopback()) { + continue; + } + + Enumeration addressEnumeration = ni.getInetAddresses(); + while (addressEnumeration.hasMoreElements()) { + InetAddress address = addressEnumeration.nextElement(); + + // ignores all invalidated addresses + if (address.isLinkLocalAddress() || address.isLoopbackAddress() || address.isAnyLocalAddress()) { + continue; + } + + return address; + } + } + + throw new RuntimeException("No validated local address!"); + } + + /** + * Retrieve local address + * + * @return the string local address + */ + public static String getLocalAddress() { + return localAddress.getHostAddress(); + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/utils/PaddedAtomicLong.java b/src/main/java/com/baidu/fsg/uid/utils/PaddedAtomicLong.java new file mode 100644 index 0000000..c338312 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/utils/PaddedAtomicLong.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.utils; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Represents a padded {@link AtomicLong} to prevent the FalseSharing problem

    + * + * The CPU cache line commonly be 64 bytes, here is a sample of cache line after padding:
    + * 64 bytes = 8 bytes (object reference) + 6 * 8 bytes (padded long) + 8 bytes (a long value) + * + * @author yutianbao + */ +public class PaddedAtomicLong extends AtomicLong { + private static final long serialVersionUID = -3415778863941386253L; + + /** Padded 6 long (48 bytes) */ + public volatile long p1, p2, p3, p4, p5, p6 = 7L; + + /** + * Constructors from {@link AtomicLong} + */ + public PaddedAtomicLong() { + super(); + } + + public PaddedAtomicLong(long initialValue) { + super(initialValue); + } + + /** + * To prevent GC optimizations for cleaning unused padded references + */ + public long sumPaddingToPreventOptimization() { + return p1 + p2 + p3 + p4 + p5 + p6; + } + +} \ No newline at end of file diff --git a/src/main/java/com/baidu/fsg/uid/utils/ValuedEnum.java b/src/main/java/com/baidu/fsg/uid/utils/ValuedEnum.java new file mode 100644 index 0000000..22a4964 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/utils/ValuedEnum.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.utils; + +/** + * {@code ValuedEnum} defines an enumeration which is bounded to a value, you + * may implements this interface when you defines such kind of enumeration, that + * you can use {@link EnumUtils} to simplify parse and valueOf operation. + * + * @author yutianbao + */ +public interface ValuedEnum { + T value(); +} diff --git a/src/main/java/com/baidu/fsg/uid/worker/DisposableWorkerIdAssigner.java b/src/main/java/com/baidu/fsg/uid/worker/DisposableWorkerIdAssigner.java new file mode 100644 index 0000000..bf98d73 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/worker/DisposableWorkerIdAssigner.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.worker; + + +import com.baidu.fsg.uid.worker.service.WorkerNodeEntityService; +import com.baidu.fsg.uid.utils.DockerUtils; +import com.baidu.fsg.uid.utils.NetUtils; +import org.apache.commons.lang3.RandomUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +import jakarta.annotation.Resource; +import java.time.LocalDate; +import java.time.LocalDateTime; +import com.baidu.fsg.uid.worker.entity.WorkerNodeEntity; +/** + * Represents an implementation of {@link WorkerIdAssigner}, + * the worker id will be discarded after assigned to the UidGenerator + * + * @author yutianbao + */ +public class DisposableWorkerIdAssigner implements WorkerIdAssigner { + private static final Logger LOGGER = LoggerFactory.getLogger(DisposableWorkerIdAssigner.class); + + @Resource + private WorkerNodeEntityService workerNodeEntityService; + /** + * Assign worker id base on database.

    + * If there is host name & port in the environment, we considered that the node runs in Docker container
    + * Otherwise, the node runs on an actual machine. + * + * @return assigned worker id + */ + @Override + @Transactional + public long assignWorkerId() { + // build worker node entity + WorkerNodeEntity workerNodeEntity = buildWorkerNode(); + + // add worker node for new (ignore the same IP + PORT) + Long id = workerNodeEntityService.save(workerNodeEntity); + LOGGER.info("Add worker node:" + workerNodeEntity); + return id; + } + + /** + * Build worker node entity by IP and PORT + */ + private WorkerNodeEntity buildWorkerNode() { + WorkerNodeEntity workerNodeEntity = new WorkerNodeEntity(); + if (DockerUtils.isDocker()) { + workerNodeEntity.setType(WorkerNodeType.CONTAINER.value()); + workerNodeEntity.setHostName(DockerUtils.getDockerHost()); + workerNodeEntity.setPort(DockerUtils.getDockerPort()); + + } else { + workerNodeEntity.setType(WorkerNodeType.ACTUAL.value()); + workerNodeEntity.setHostName(NetUtils.getLocalAddress()); + workerNodeEntity.setPort(System.currentTimeMillis() + "-" + RandomUtils.nextInt(0,100000)); + } + workerNodeEntity.setLaunchDate(LocalDate.now()); + workerNodeEntity.setCreated(LocalDateTime.now()); + workerNodeEntity.setModified(LocalDateTime.now()); + + return workerNodeEntity; + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/worker/WorkerIdAssigner.java b/src/main/java/com/baidu/fsg/uid/worker/WorkerIdAssigner.java new file mode 100644 index 0000000..7119f20 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/worker/WorkerIdAssigner.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.worker; +import com.baidu.fsg.uid.impl.DefaultUidGenerator; + + + +/** + * Represents a worker id assigner for {@link DefaultUidGenerator} + * + * @author yutianbao + */ +public interface WorkerIdAssigner { + + /** + * Assign worker id for {@link DefaultUidGenerator} + * + * @return assigned worker id + */ + long assignWorkerId(); + +} diff --git a/src/main/java/com/baidu/fsg/uid/worker/WorkerNodeType.java b/src/main/java/com/baidu/fsg/uid/worker/WorkerNodeType.java new file mode 100644 index 0000000..3d86c72 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/worker/WorkerNodeType.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baidu.fsg.uid.worker; + + +import com.baidu.fsg.uid.utils.ValuedEnum; + +/** + * WorkerNodeType + *

  • CONTAINER: Such as Docker + *
  • ACTUAL: Actual machine + * + * @author yutianbao + */ +public enum WorkerNodeType implements ValuedEnum { + + CONTAINER(1), ACTUAL(2); + + /** + * Lock type + */ + private final Integer type; + + /** + * Constructor with field of type + */ + private WorkerNodeType(Integer type) { + this.type = type; + } + + @Override + public Integer value() { + return type; + } + +} diff --git a/src/main/java/com/baidu/fsg/uid/worker/entity/WorkerNodeEntity.java b/src/main/java/com/baidu/fsg/uid/worker/entity/WorkerNodeEntity.java new file mode 100644 index 0000000..90dcbc3 --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/worker/entity/WorkerNodeEntity.java @@ -0,0 +1,92 @@ +package com.baidu.fsg.uid.worker.entity; + + + +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * @author jason + * @since 2020-10-26 + */ + +public class WorkerNodeEntity { + + private static final long serialVersionUID = 1L; + + private Long id; + + + private String hostName; + + + private String port; + + private Integer type; + + private LocalDate launchDate; + + + private LocalDateTime modified; + + + private LocalDateTime created; + + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public LocalDate getLaunchDate() { + return launchDate; + } + + public void setLaunchDate(LocalDate launchDate) { + this.launchDate = launchDate; + } + + public LocalDateTime getModified() { + return modified; + } + + public void setModified(LocalDateTime modified) { + this.modified = modified; + } + + public LocalDateTime getCreated() { + return created; + } + + public void setCreated(LocalDateTime created) { + this.created = created; + } +} diff --git a/src/main/java/com/baidu/fsg/uid/worker/service/WorkerNodeEntityService.java b/src/main/java/com/baidu/fsg/uid/worker/service/WorkerNodeEntityService.java new file mode 100644 index 0000000..8342a5f --- /dev/null +++ b/src/main/java/com/baidu/fsg/uid/worker/service/WorkerNodeEntityService.java @@ -0,0 +1,22 @@ +package com.baidu.fsg.uid.worker.service; + + +import com.baidu.fsg.uid.worker.entity.WorkerNodeEntity; + +/** + *

    + * 数据库操作解耦 接口 + *

    + * + * @author jason + * @since 2021-10-1 + */ +public interface WorkerNodeEntityService { + + /** + * 保存实体 + * + * @param workerNodeEntity + */ + Long save(WorkerNodeEntity workerNodeEntity); +} diff --git a/src/main/java/com/techsor/datacenter/sender/TechsorDataCenterSenderApplication.java b/src/main/java/com/techsor/datacenter/sender/TechsorDataCenterSenderApplication.java new file mode 100644 index 0000000..0b2c7ab --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/TechsorDataCenterSenderApplication.java @@ -0,0 +1,21 @@ +package com.techsor.datacenter.sender; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.scheduling.annotation.EnableScheduling; + +@EnableScheduling +@SpringBootApplication +@EnableCaching +public class TechsorDataCenterSenderApplication { + private static final Logger logger = LoggerFactory.getLogger(TechsorDataCenterSenderApplication.class); + + public static void main(String[] args) { + SpringApplication.run(TechsorDataCenterSenderApplication.class, args); + logger.info("TechsorDataCenterSenderApplication started successfully!"); + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/compiler/JsonPathExtractor.java b/src/main/java/com/techsor/datacenter/sender/compiler/JsonPathExtractor.java new file mode 100644 index 0000000..d1126d4 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/compiler/JsonPathExtractor.java @@ -0,0 +1,119 @@ +package com.techsor.datacenter.sender.compiler; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +/** + * JsonPath提取器类,用于将包含算术运算、函数和JsonPath的中缀表达式转换为逆波兰表示法(RPN)。 + *

    + * 这个类使用正则表达式来分割输入的字符串表达式,并基于算法将其转换为RPN队列,以便后续的评估和处理。 + * 支持的功能包括基本的算术运算(+、-、*、/)和自定义函数。 + */ +public class JsonPathExtractor { + + private static final Set FUNCTIONS = new HashSet<>(); + + static { + Method[] declaredMethods = UserFunction.class.getDeclaredMethods(); + for (Method declaredMethod : declaredMethods) { + FUNCTIONS.add(declaredMethod.getName()); + } + } + private boolean containsArithmeticOperator(String s) { + return s.contains("+") || s.contains("-") || s.contains("*") || s.contains("/"); + } + + private boolean containsFunction(String s) { + return FUNCTIONS.contains(s); + } + /** + * 将中缀表达式转换为逆波兰表示法(RPN)。 + *

    + * 使用Shunting-yard算法来处理中缀表达式,并将其转换为后缀表示法,以便于表达式的评估。 + * + * @param infix 中缀表达式字符串 + * @return 转换后的RPN表达式队列 + */ + public static Queue infixToRPN(String infix) { + Stack operators = new Stack<>(); + Queue output = new LinkedList<>(); + + // 使用正则表达式来分割字符串,这样可以同时处理数字、运算符、括号、JsonPath和函数 + Pattern pattern = Pattern.compile("\\$\\..+?(?=\\+|\\-|\\*|\\/|\\)|$)|\\d+|[-+*/()]|" + String.join("|", FUNCTIONS)); + Matcher matcher = pattern.matcher(infix); + + while (matcher.find()) { + String token = matcher.group(); + if (isNumber(token)) { + output.add(token); + } else if (isJsonPath(token)) { + output.add(token); + } else if (FUNCTIONS.contains(token)) { + operators.push(token); + } else if ("(".equals(token)) { + operators.push(token); + } else if (")".equals(token)) { + while (!operators.isEmpty() && !operators.peek().equals("(")) { + output.add(operators.pop()); + } + operators.pop(); // Pop "(" + if (!operators.isEmpty() && FUNCTIONS.contains(operators.peek())) { + output.add(operators.pop()); // Pop function + } + } else { // Handle operators + while (!operators.isEmpty() && isHigherPrecedence(operators.peek(), token)) { + output.add(operators.pop()); + } + operators.push(token); + } + } + + while (!operators.isEmpty()) { + output.add(operators.pop()); + } + + return output; + } + /** + * 比较两个运算符的优先级。 + * + * @param op1 第一个运算符 + * @param op2 第二个运算符 + * @return 如果第一个运算符的优先级高于或等于第二个运算符则返回true,否则返回false + */ + private static boolean isHigherPrecedence(String op1, String op2) { + if ("+".equals(op1) || "-".equals(op1)) { + return ("*".equals(op2) || "/".equals(op2)); + } + return false; + } + + /** + * 检查给定的字符串是否是JsonPath。 + * + * @param s 待检查的字符串 + * @return 如果字符串是JsonPath则返回true,否则返回false + */ + private static boolean isJsonPath(String s) { + return s.startsWith("$."); + } + + /** + * 检查给定的字符串是否可以解析为数字。 + * + * @param s 待检查的字符串 + * @return 如果字符串可以解析为数字则返回true,否则返回false + */ + private static boolean isNumber(String s) { + try { + Double.parseDouble(s); + return true; + } catch (NumberFormatException e) { + return false; + } + } +} + + + diff --git a/src/main/java/com/techsor/datacenter/sender/compiler/MvelCompiler.java b/src/main/java/com/techsor/datacenter/sender/compiler/MvelCompiler.java new file mode 100644 index 0000000..43dae25 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/compiler/MvelCompiler.java @@ -0,0 +1,71 @@ +package com.techsor.datacenter.sender.compiler; + + +import org.apache.commons.lang3.StringUtils; +import org.mvel2.MVEL; +import org.mvel2.ParserContext; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * MVEL编译器类,用于编译包含自定义函数的MVEL表达式。 + *

    + * 该类实现了单例模式,确保整个应用中只有一个MVEL编译器实例。 + * 它预先加载了UserFunction类中定义的所有方法到MVEL解析上下文中,以便在表达式中使用这些自定义函数。 + */ +public class MvelCompiler { + + private static final MvelCompiler instance = new MvelCompiler(); + + private static ParserContext context; + + /** + * 私有构造函数,初始化MVEL编译器实例。 + *

    + * 在构造函数中,创建一个ParserContext对象,并将UserFunction类中定义的所有方法加载到这个上下文中。 + */ + private MvelCompiler() { + context = new ParserContext(); + + // 将UserFunction类中的所有方法加载到context中 + Method[] declaredMethods = UserFunction.class.getDeclaredMethods(); + for (Method declaredMethod : declaredMethods) { + context.addImport(declaredMethod.getName(), declaredMethod); + } + } + /** + * 获取MvelCompiler类的唯一实例。 + * + * @return MvelCompiler的单例对象 + */ + public static MvelCompiler getInstance() { + return instance; + } + /** + * 获取MVEL编译器的解析上下文。 + * + * @return MVEL解析上下文 + */ + public static ParserContext getContext() { + return context; + } + /** + * 编译给定的MVEL表达式。 + *

    + * 如果表达式非空,则使用预先配置的解析上下文来编译表达式,并返回一个可序列化的编译表达式对象。 + * + * @param expression 待编译的MVEL表达式字符串 + * @return 编译后的可序列化表达式对象,如果输入表达式为空,则返回null + */ + public Serializable compile(String expression) { + if (StringUtils.isNotEmpty(expression)) { + return MVEL.compileExpression(expression, context); + } + + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/compiler/MvelExecutor.java b/src/main/java/com/techsor/datacenter/sender/compiler/MvelExecutor.java new file mode 100644 index 0000000..9246e7e --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/compiler/MvelExecutor.java @@ -0,0 +1,37 @@ +package com.techsor.datacenter.sender.compiler; + + +import lombok.extern.slf4j.Slf4j; +import org.mvel2.MVEL; + +import java.util.Map; + +/** + * MVEL表达式执行器类,用于执行MVEL表达式。 + *

    + * 该类提供了一个静态方法来执行给定的MVEL表达式,并允许传入参数映射来定义表达式中使用的变量。 + */ +@Slf4j +public class MvelExecutor { + + + /** + * 执行给定的MVEL表达式,并返回执行结果。 + *

    + * 该方法使用 {@link MvelCompiler} 提供的解析上下文来编译表达式,然后使用提供的参数映射执行编译后的表达式。 + * 如果执行过程中发生异常,将记录错误日志。 + * + * @param mvel 表达式字符串 + * @param params 表达式中使用的变量的参数映射 + * @return 表达式执行的结果,如果执行失败返回null + */ + public static Object eval(String mvel, Map params) { + Object result = null; + try { + result = MVEL.executeExpression(MVEL.compileExpression(mvel, MvelCompiler.getContext()), params); + } catch (Exception e) { + log.error("解析失败mvel ========>{}",mvel,e); + } + return String.valueOf(result); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/compiler/UserFunction.java b/src/main/java/com/techsor/datacenter/sender/compiler/UserFunction.java new file mode 100644 index 0000000..a08f16d --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/compiler/UserFunction.java @@ -0,0 +1,368 @@ +package com.techsor.datacenter.sender.compiler; + + +import com.techsor.datacenter.sender.service.*; +import com.techsor.datacenter.sender.utils.SpringUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +/** + * 提供静态方法处理数据字符串的工具类。 + */ +@Slf4j +public class UserFunction { + /** + * 使用 OCRInnerService 处理 OCR 数据字符串,并返回处理后的数据。 + * + * @param data 要处理的 OCR 数据字符串。 + * @return 处理后的 OCR 数据字符串,若发生错误,则返回原始数据字符串。 + */ + public static String getOCRDataString(String data){ + OCRInnerService ocrInnerService=SpringUtils.getBean("ocrInnerService", OCRInnerService.class); + try { + return ocrInnerService.start(data); + } catch (Exception e) { + log.error("getOCRDataString error",e); + } + return data; + } + /** + * 使用 NBIInnerService 处理 NBI 数据字符串,并返回处理后的数据。 + * + * @param data 要处理的 NBI 数据字符串。 + * @return 处理后的 NBI 数据字符串,若发生错误,则返回空字符串。 + */ + public static String getNBIDataString(String data){ + NBIInnerService deltaInnerService=SpringUtils.getBean("nbiInnerService", NBIInnerService.class); + try { + return deltaInnerService.start(data); + } catch (Exception e) { + log.error("getNBIDataString error",e); + } + return ""; + } + + public static String getDeltaDataString(String data){ + StringBuffer stringBuffer=new StringBuffer(); + stringBuffer.append("["); + stringBuffer.append(data); + stringBuffer.append("]"); + DeltaInnerService deltaInnerService=SpringUtils.getBean("deltaInnerService", DeltaInnerService.class); + try { + return deltaInnerService.start(stringBuffer.toString()); + } catch (Exception e) { + log.error("getDeltaDataString error",e); + } + return ""; + } + /** + * 使用 KingIOServerService 处理 KingIO 数据字符串,并返回处理后的数据。 + * + * @param data 要处理的 KingIO 数据字符串。 + * @return 处理后的 KingIO 数据字符串,若发生错误,则返回空字符串。 + */ + public static String getKingIODataString(String data){ + KingIOServerService kingIOServerService=SpringUtils.getBean("kingIOServerService", KingIOServerService.class); + try { + return kingIOServerService.start(data); + } catch (Exception e) { + log.error("getKingIODataString error",e); + } + return ""; + } + + /** + * 使用 St150Service 处理 st150 数据字符串,并返回处理后的数据。 + * + * @param data 要处理的 KingIO 数据字符串。 + * @return 处理后的 KingIO 数据字符串,若发生错误,则返回空字符串。 + */ + public static String getSt150DataString(String data){ + St150Service st150Service=SpringUtils.getBean("St150Service", St150Service.class); + try { + return st150Service.start(data); + } catch (Exception e) { + log.error("getSt150DataString error",e); + } + return ""; + } + + /** + * 使用 NittanService 处理 Nittan 数据字符串,并返回处理后的数据。 + * + * @param data 要处理的 Nittan 数据字符串。 + * @return 处理后的 Nittan 数据字符串,若发生错误,则返回空字符串。 + */ + public static String getNittanDataString(String data){ + NittanService nittanInnerServiceInstance=SpringUtils.getBean("nittanService", NittanService.class); + try { + return nittanInnerServiceInstance.start(data); + } catch (Exception e) { + log.error("getNittanDataString error",e); + } + return ""; + } + public static String getZetaDataString(String data){ + ZETAInnerService ZETAInnerServiceInstance=SpringUtils.getBean("ZETAInnerService",ZETAInnerService.class); + return ZETAInnerServiceInstance.start(data); + } + + public static String subString(String data, String start, String end) { + int s = Integer.parseInt(start); + int e = Integer.parseInt(end); + return StringUtils.substring(data, s, e); + } + + /*** + * 函数从左边开始字符串匹配 + * **/ + public static boolean startWith(String data, String start) { + + return StringUtils.startsWith(data, start); + } + /** + * + * 判断字符串是否以end为结尾 + * **/ + public static boolean endWith(String data, String end) { + + return StringUtils.endsWith(data, end); + } + + /** + * 判断字符串是否包含containsStr + * **/ + public static boolean contains(String data, String containsStr) { + + return StringUtils.contains(data, containsStr); + } + + /** + * data转换为16进制 + * **/ + public static String toHex(String data) { + return Integer.toHexString(Integer.parseInt(data)); + } + + /** + * + * */ + public static String toOctal(String data) { + return Integer.toOctalString(Integer.parseInt(data)); + } + + public static String toBinary(String data) { + return Integer.toBinaryString(Integer.parseInt(data)); + } + + public static String toDecimal(String data) { + return Integer.toString(Integer.parseInt(data)); + } + + + public static String toHex(String data, String length) { + int len = Integer.parseInt(length); + String tmp = Integer.toHexString(Integer.parseInt(data)); + return StringUtils.leftPad(tmp, len, "0"); + } + + public static String toOctal(String data, String length) { + int len = Integer.parseInt(length); + String tmp = Integer.toOctalString(Integer.parseInt(data)); + return StringUtils.leftPad(tmp, len, "0"); + } + + /** + * 二进制转为十进制 + * + * **/ + public static Integer binaryToDecimal(String data) { + return Integer.parseInt(data, 2); + } + + /** + * + *十六进制转为十进制 + * **/ + public static Integer hexToDecimal(String data) { + return Integer.parseInt(data, 16); + } + + /** + * 第N位为 + * @param data 数据字段 random + * @param n N位 80 + * @param value 值 (1,2,3,4) 只要其中一个满足,就返回true + * @return + */ + public static boolean nthBitEq(String data, String n, String value) { + int m = Integer.parseInt(n); + String tmp = data.substring(m-1, m); + String[] aars = value.split(","); + + boolean rs = false; + for (String arr : aars) { + rs = rs || tmp.equals(arr.trim()); + } + return rs; + } + + /** + * 第N位不为 + * @param data 数据字段 random + * @param n N位 80 + * @param value 值 (1,2,3,4) 只要其中一个满足,就返回false + * @return + */ + public static boolean nthBitNq(String data, String n, String value) { + return !nthBitEq(data,n,value); + } + + /** + * 第N位介于 [5,8] >=5 and <=8 + * @param data 数据字段 random + * @param n N位 80 + * @param sv 开始值 5 + * @param ev 结束值 8 + * @return + */ + public static boolean nthBitBt(String data, String n, String sv, String ev) { + int m = Integer.parseInt(n); + int tmp = Integer.valueOf(data.substring(m-1, m)); + return tmp >= Integer.valueOf(sv) && tmp <= Integer.valueOf(ev); + } + + + /** + * 倒数第N位为 + * @param data 数据字段 random + * @param n 倒数第N位 + * @param value 值(1,2,3,4) 只要其中一个满足,就返回true + * @return + */ + public static boolean enBitEq(String data, String n, String value) { + int m = data.length() - Integer.parseInt(n) + 1; + String tmp = data.substring(m-1, m); + String[] airs = value.split(","); + + boolean rs = false; + for (String arr : airs) { + rs = rs || tmp.equals(arr.trim()); + } + return rs; + } + + /** + * 倒数第N位不为 + * @param data 数据字段 random + * @param n 倒数第N位 + * @param value 值(1,2,3,4) 只要其中一个满足,就返回false + * @return + */ + public static boolean enBitNq(String data, String n, String value) { + return !enBitEq(data,n,value); + } + + /** + * 倒数第N位介于 [5,8] >=5 and <=8 + * @param data 数据字段 random + * @param n N位 80 + * @param sv 开始值 5 + * @param ev 结束值 8 + * @return + */ + public static boolean enBitBt(String data, String n, String sv, String ev) { + int m = data.length() - Integer.parseInt(n) + 1; + int tmp = Integer.valueOf(data.substring(m-1, m)); + return tmp >= Integer.valueOf(sv) && tmp <= Integer.valueOf(ev); + } + + /** + * 尾号为 + * @param data 数据字段 mid + * @param value 值(1,2,3,4) 只要其中一个满足,就返回true + * @return + */ + public static boolean tailEq(String data, String value) { + String[] airs = value.split(","); + boolean rs = false; + for (String arr : airs) { + rs = rs || data.endsWith(arr.trim()); + } + return rs; + } + + /** + * 尾号不为 + * @param data 数据 + * @param value 值(1,2,3,4) 只要其中一个满足,就返回false + * @return + */ + public static boolean tailNq(String data, String value) { + return !tailEq(data, value); + } + + /** + * 尾号介于 [5,8] >=5 and <=8 + * @param data 数据字段 mid + * @param sv 开始值 5 + * @param ev 结束值 8 + * @return + */ + public static boolean tailBt(String data, String sv, String ev) { + int tmp = Integer.valueOf(data.substring(data.length()-1, data.length())); + return tmp >= Integer.valueOf(sv) && tmp <= Integer.valueOf(ev); + } + + /** + * 包含 + * @param data 数据字段 channel + * @param value 值(F,D,E) 包含其中之一就返回true + * @return + */ + public static boolean defContains(String data, String value) { + String[] arcs = value.split(","); + + boolean rs = false; + for (String arr : arcs) { + rs = rs || data.contains(arr.trim()); + } + return rs; + } + + /** + * 不包含 + * @param data 数据字段 channel + * @param value 值(F,D,E) 包含其中之一就返回false + * @return + */ + public static boolean defNotContains(String data, String value) { + return !defContains(data, value); + } + + + /** + * 布尔值为真 + * @param data 数据字段 + */ + public static boolean isTrue(String data) { + return Boolean.parseBoolean(data); + } + + /** + * 布尔值为假 + * @param data 数据字段 + */ + public static boolean isFalse(String data) { + return !Boolean.parseBoolean(data); + } + + + public static Integer getBooleanIntValue(String data){ + if(Boolean.parseBoolean(data)){ + return 1; + } + return 0; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/components/CommonOpt.java b/src/main/java/com/techsor/datacenter/sender/components/CommonOpt.java new file mode 100644 index 0000000..a7404bc --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/components/CommonOpt.java @@ -0,0 +1,248 @@ +package com.techsor.datacenter.sender.components; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.techsor.datacenter.sender.constants.Constants; +import com.techsor.datacenter.sender.dao.CompanyInfoDao; +import com.techsor.datacenter.sender.dao.DeviceDao; +import com.techsor.datacenter.sender.dto.RedisAlarmDTO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.company.CompanyEntity; +import com.techsor.datacenter.sender.utils.HttpUtil; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import jakarta.annotation.Resource; +import java.util.*; + +@Component +public class CommonOpt { + + private static final Logger log= LoggerFactory.getLogger(CommonOpt.class); + + private static final String ALERT_CANCEL_INFO = Constants.ALERT_CANCEL_INFO; + private static final String ALERT_INFO = Constants.ALERT_INFO; + + @Resource + private RedisTemplate redisTemplate; + + @Resource + private CompanyInfoDao companyInfoDao; + @Resource + DeviceDao deviceDao; + + + @Resource + private Constants constants; + + /** + * Retrieve the company ID associated with the device ID from Redis. + * + * @param deviceId The unique identifier of the device. + * @return The associated company ID, or null if no matching company ID is found. + */ + public String getTopCompanyId(String deviceId){ + LettuceConnectionFactory connectionFactory = (LettuceConnectionFactory) redisTemplate.getConnectionFactory(); +// log.error("Not Error, For Debug,[redisTemplate] Current Redis Database: " + connectionFactory.getDatabase()); +// log.error("Not Error, For Debug,[deviceId]: " + deviceId); + String companyId = (String) redisTemplate.opsForHash().get("deviceCompanyMapping", deviceId); + if (companyId==null){ + //Try from mysql + companyId = getCompanyIdFromAllDB(deviceId); + if (companyId.equals("")){ + log.error("Can't find topCompanyId for deviceId: "+deviceId+", Please notice if this device is not login on platform."); + return null; + }else{ + //Update redis cache + redisTemplate.opsForHash().put("deviceCompanyMapping", deviceId, companyId); + } + } + try{ + companyId = companyInfoDao.getTopCompanyId(companyId)+""; + }catch (Exception e){ + log.error("Get top companyId Error:",e.getMessage(),e.fillInStackTrace()); + } + log.info("GetTopCompanyId:"+companyId); + return companyId; + } + + + + public String getCompanyIdFromAllDB(String deviceId){ + String companyId = ""; + List companyEntityList = companyInfoDao.getCompanyList(); + for (CompanyEntity companyItem : companyEntityList){ + String tempCompanyId = ""; + log.info("[getCompanyIdFromAllDB] companyId:"+companyItem.getId().toString()); + if (companyItem.getParentId()==-1 || companyItem.getParentId()==1){ + tempCompanyId = companyItem.getId().toString(); + }else{ + log.info("[getCompanyIdFromAllDB] Not level 1 company. skip."); + continue; + } + //只处理一级企业 + log.info("[getCompanyIdFromAllDB] deviceId:"+deviceId+" comanyId:"+tempCompanyId); + List deviceList = deviceDao.getDeviceInfoByDeviceIdWithDB(deviceId,tempCompanyId); + if (deviceList.size()<=0){ + log.info("[getCompanyIdFromAllDB] deviceList size is 0"); + continue; + }else{ + log.info("[getCompanyIdFromAllDB] companyId get! Value:"+tempCompanyId); + companyId=tempCompanyId; + break; + } + } + log.info("GetCompanyId:"+companyId); + return companyId; + } + + public void handleTargetUrl(JSONObject respObject, Map keys) { + if (keys.containsKey("needTransfer") && "true".equalsIgnoreCase(keys.get("needTransfer").toString())) { + String targetData = JSONObject.toJSONString(keys.get("rawData")); + List targetUrlList = new ArrayList<>(); + + JSONArray dataArr = respObject.getJSONObject("data").getJSONArray("dataList"); + if (200 == respObject.getIntValue("code") && null != dataArr) { + for (Object data : dataArr) { + String targetForwardParamsStr = JSONObject.parseObject(data.toString()).getString("targetForwardParams"); + if (!StringUtils.isEmpty(targetForwardParamsStr)) { + JSONObject tfpObject = JSONObject.parseObject(targetForwardParamsStr); + targetUrlList.add(tfpObject.getString("url")); + } + } + } + + + if (null != targetUrlList && !targetUrlList.isEmpty()) { + for (String targetUrl : targetUrlList) { + if (!StringUtils.isEmpty(targetUrl)) { + String pushResp = HttpUtil.doPost(targetUrl, targetData, null); + System.out.println("push alarm result:" + pushResp); + } + } + } else { + System.out.println("alarm push url is null........."); + } + } + } + + public String getEmailSubject(String alarmType, int type, Map keys, + JSONObject alertForwardConfig) { + String buildingName = ""; + if (keys.containsKey("buildingInfo") && null != keys.get("buildingInfo")) { + JSONObject obj = JSONObject.parseObject(JSONObject.toJSONString(keys.get("buildingInfo"))); + buildingName = obj.getString("name"); + } + + String floorName = ""; + if (keys.containsKey("floorInfo") && null != keys.get("floorInfo")) { + JSONObject obj = JSONObject.parseObject(JSONObject.toJSONString(keys.get("floorInfo"))); + floorName = obj.getString("name"); + } + + String spaceName = ""; + if (keys.containsKey("spaceInfo") && null != keys.get("spaceInfo")) { + JSONObject obj = JSONObject.parseObject(JSONObject.toJSONString(keys.get("spaceInfo"))); + spaceName = obj.getString("name"); + } + if (1 == type) { + return String.format(ALERT_INFO, buildingName, floorName, spaceName, alarmType); + } else if (2 == type) { + return String.format(ALERT_CANCEL_INFO, buildingName, floorName, spaceName, alarmType); + } + return ""; + } + + + + public void switchAlarmSendMail(int type, String emails, String parseAlertContent, String subject, String deviceId) { + String defaultMemo = ""; + if (2 == type) { + defaultMemo = "設備が正常に戻りました"; + } + + String sendHtml = getMemo(parseAlertContent, defaultMemo); + List emailList = Arrays.asList(emails.contains(",") ? StringUtils.split(emails, ",") : new String[]{emails}); + List sendList = new ArrayList(emailList); + //收件人 + String receiver = sendList.get(0); + //抄送人 + sendList.remove(receiver); + String cc = StringUtils.collectionToDelimitedString(sendList, ","); + + String errorMsg = ""; + try { + SendMail sendMail = new SendMail(); + sendMail.sendMail(subject, sendHtml, receiver, cc); + } catch (Exception e) { + System.out.println("mail sent error:" + e.getMessage()); + errorMsg = e.getMessage(); + } + //统计邮件成功/失败数量 + JSONObject postJsonObject = new JSONObject(); + postJsonObject.put("errorMsg", errorMsg); + postJsonObject.put("deviceId", deviceId); + if (StringUtils.isEmpty(errorMsg)) { + postJsonObject.put("status", "Succeeded"); + } else { + postJsonObject.put("status", "Failed"); + } + String resp = HttpUtil.doPost(constants.EMAIL_RESULT_URL, postJsonObject.toString() , null); + System.out.println("log email result: " + resp); + } + + + + + public static String getMemo(String parseAlertContent, String defaultMemo) { + return StringUtils.isEmpty(parseAlertContent) ? defaultMemo : parseAlertContent; + } + + + + public void roid(String parseAlertContent, String emailSubject, Map keys, int type, + RedisAlarmDTO redisAlarmDTO) { + HashMap headerMap = new HashMap<>(); + headerMap.put("Content-Type", "application/json;charset=UTF-8"); + headerMap.put("access-control-allow-credentials", "True"); + headerMap.put("X-Requested-With", "XMLHttpRequest"); + headerMap.put("Authorization", constants.ROID_AUTHORIZATION); + headerMap.put("Accept", "application/json, text/plain, /"); + if (1 == type) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("problem_report_category_id", Long.valueOf(keys.get("problemReportCategoryId").toString())); + jsonObject.put("target_id", Long.valueOf(keys.get("targetId").toString())); + jsonObject.put("memo", CommonOpt.getMemo(parseAlertContent, "").replace("
    ", " ")); + if(keys.containsKey("buildingId")) { + jsonObject.put("building_id", Long.valueOf(keys.get("buildingId").toString())); + } +// if(keys.containsKey("buildingCode")) { +// jsonObject.put("building_code", keys.get("buildingCode").getS()); +// } + + String resp = HttpUtil.doPost(constants.ROID_ALARM_URL, jsonObject.toString(), headerMap); + System.out.println("roid alarm api resp : " + resp); + //redis设置状态和id + if (!StringUtils.isEmpty(resp)) { + JSONObject roidRespObject = JSONObject.parseObject(resp); + if (roidRespObject.containsKey("id")) { + redisAlarmDTO.setProblemReportId(roidRespObject.getLongValue("id")); + } + } + } else if (2 == type) { + if (null != redisAlarmDTO && null != redisAlarmDTO.getProblemReportId()) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("problem_report_id", redisAlarmDTO.getProblemReportId()); + jsonObject.put("memo", CommonOpt.getMemo(parseAlertContent, emailSubject).replace("
    ", " ")); + + String resp = HttpUtil.doPost(constants.ROID_ALARM_CANCEL_URL, jsonObject.toString(), headerMap); + System.out.println("roid cancel alarm api resp : " + resp); + } + } + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/components/DBMRestfulClient.java b/src/main/java/com/techsor/datacenter/sender/components/DBMRestfulClient.java new file mode 100644 index 0000000..a82128c --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/components/DBMRestfulClient.java @@ -0,0 +1,57 @@ +package com.techsor.datacenter.sender.components; + + +import com.techsor.datacenter.sender.config.DataCenterEnvConfig; +import com.techsor.datacenter.sender.entitiy.DBMResponse; + +import cn.hutool.http.HttpRequest; +import cn.hutool.json.JSONUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import jakarta.annotation.Resource; +import java.util.Locale; + +@Component +public class DBMRestfulClient { + private static final Logger logger = LoggerFactory.getLogger(DBMRestfulClient.class); + + @Resource + private DataCenterEnvConfig dataCenterEnvConfig; + + @Resource + private RestTemplate restTemplate; + + + //post传递json参数 + public DBMResponse postJson(String sessionId, String params) { + logger.debug("sessionId: " +sessionId); + params = params.toLowerCase(Locale.ROOT); + //请求参数JOSN类型 + HttpHeaders headers = new HttpHeaders(); + MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8"); + headers.setContentType(type); +// HttpEntity formEntity = new HttpEntity(params, headers); + logger.debug("Post URL: {},{}" ,dataCenterEnvConfig.getJgUrl(),sessionId); + logger.debug("Post msg: {}" ,params); + + + //转发数据到DBM的正式代码,测试期间注释掉 + String responseJson = HttpRequest.post(dataCenterEnvConfig.getJgUrl() + sessionId) + .body(params) + .execute() + .body(); + + // 将JSON字符串转换为DBMResponse对象 + DBMResponse response = JSONUtil.parseObj(responseJson).toBean(DBMResponse.class); + logger.debug("Response Code:{}",response.getCode()); + logger.debug("Response msg:{}",response.getMsg()); + return response; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/components/DBMWSClient.java b/src/main/java/com/techsor/datacenter/sender/components/DBMWSClient.java new file mode 100644 index 0000000..dcd834f --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/components/DBMWSClient.java @@ -0,0 +1,97 @@ +package com.techsor.datacenter.sender.components; + + +import com.jurassic.godzilla.sdk.ws.GodzillaWSClientBuilder; +import com.techsor.datacenter.sender.dao.NittanDAO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * @Author: hao.dai + * @Project: websocket + * @Date: 2021/5/10 14:47 + * @Description: + * @Godzilla.tech + */ +public class DBMWSClient { + private final static Logger logger = LoggerFactory.getLogger(DBMWSClient.class); + public static ScheduledExecutorService executorService; + public static List> godzillaWSClientBuilderList = new ArrayList<>(); + + + public static void main(String[] args) throws URISyntaxException, InterruptedException { + GodzillaWSClientBuilder godzillaWSClientBuilder = GodzillaWSClientBuilder.create() + .deviceId("RBTEST0001") + .autoReconnect(true) + .sendHBIntervalTime(1000) + .reconnectIntervalTime(1000) + .godzillaWSDataHandler(message -> { + System.out.println("接收到的消息:" + message); + }) + .build(new URI("wss://agent-slb-1.godzilla.tech/")); + godzillaWSClientBuilder.connectBlocking(); + ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + //10s一次 + executorService.scheduleWithFixedDelay(() -> { + godzillaWSClientBuilder.send("{\n" + + " \"temperature\": 25,\n" + + " \"city\": \"Tokyo\"\n" + + "}"); + }, 100L, 10000, TimeUnit.MILLISECONDS); + Thread.sleep(90000000); + } + + public static void initWS() throws URISyntaxException, InterruptedException { + NittanDAO nittanDAO = new NittanDAO(); + List> wsClientList = nittanDAO.getJGWSClient(); + for (int i=0;i { + System.out.println("接收到的消息:" + message); + }) + .build(new URI("wss://agent-slb-1.godzilla.tech/")); + godzillaWSClientBuilder.connectBlocking(); + executorService = Executors.newSingleThreadScheduledExecutor(); + + //将client_id与客户端实例绑定返回 + Map item = new HashMap<>(); + item.put((String) wsClientList.get(i).get("client_id"),godzillaWSClientBuilder); + godzillaWSClientBuilderList.add(item); + } + + } + + public static void sendMessage(String clientId,String content) { + if(DBMWSClient.godzillaWSClientBuilderList!=null && DBMWSClient.godzillaWSClientBuilderList.size()>0){ + for (Map item:DBMWSClient.godzillaWSClientBuilderList + ) { + if (item.get(clientId)!=null){ + logger.debug("Use client:"+clientId); + item.get(clientId).send(content); + } + } + } + } + + public static void sendMessage(String content) { + if(DBMWSClient.godzillaWSClientBuilderList!=null && DBMWSClient.godzillaWSClientBuilderList.size()>0){ + logger.debug("Use default client:"+"NEC00003"); + DBMWSClient.godzillaWSClientBuilderList.get(0).get("NEC00003").send(content); + logger.debug("Send message: "+content); + } + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/components/GlobalSwitchStatusComponent.java b/src/main/java/com/techsor/datacenter/sender/components/GlobalSwitchStatusComponent.java new file mode 100644 index 0000000..ddfd76e --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/components/GlobalSwitchStatusComponent.java @@ -0,0 +1,26 @@ +package com.techsor.datacenter.sender.components; + +import org.springframework.stereotype.Component; + +import jakarta.annotation.PostConstruct; +import java.util.concurrent.atomic.AtomicBoolean; + +@Component +public class GlobalSwitchStatusComponent { + + private AtomicBoolean switchStatus; + + @PostConstruct + public void init(){ + this.switchStatus=new AtomicBoolean(false); + } + + + public void updateSwitchStatus(boolean switchStatus){ + this.switchStatus=new AtomicBoolean(switchStatus); + } + + public boolean getSwitchStatus(){ + return this.switchStatus.get(); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/components/GuavaRedisCache.java b/src/main/java/com/techsor/datacenter/sender/components/GuavaRedisCache.java new file mode 100644 index 0000000..2e8c53c --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/components/GuavaRedisCache.java @@ -0,0 +1,134 @@ +package com.techsor.datacenter.sender.components; + +import com.alibaba.fastjson2.JSON; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import jakarta.annotation.PostConstruct; +import java.time.Duration; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +public class GuavaRedisCache { + + private final LoadingCache cache; + private final RedisTemplate redisTemplate; + private final GlobalSwitchStatusComponent globalSwitchStatusComponent; + + @Autowired + public GuavaRedisCache(RedisTemplate redisTemplate, GlobalSwitchStatusComponent globalSwitchStatusComponent) { + this.redisTemplate = redisTemplate; + this.globalSwitchStatusComponent = globalSwitchStatusComponent; + this.cache = CacheBuilder.newBuilder() + .maximumSize(2000) + .expireAfterWrite(7, TimeUnit.MINUTES) + .concurrencyLevel(1000) + .build(new CacheLoader<>() { + @Override + public Integer load(String key) throws Exception { + return 0; + } + }); + } + + public void incrementDailyDeviceIdCount(String deviceId) { + // if (this.globalSwitchStatusComponent.getSwitchStatus()) { + String key = getKeyForToday(); + cache.put(key + ":" + deviceId, cache.getUnchecked(key + ":" + deviceId) + 1); + // } + } + + public void incrementDailyAlertCancelCount(String deviceId) { + // if (this.globalSwitchStatusComponent.getSwitchStatus()) { + String key = getSenderAlertCancelKeyForToday(); + cache.put(key + ":" + deviceId, cache.getUnchecked(key + ":" + deviceId) + 1); + // } + } + + public void incrementDailyBuildingCount(String deviceId) { + // if (this.globalSwitchStatusComponent.getSwitchStatus()) { + String key = getSenderBuildingKeyForToday(); + cache.put(key + ":" + deviceId, cache.getUnchecked(key + ":" + deviceId) + 1); + // } + } + + public void incrementDailyDispatchCount(String deviceId) { + // if (this.globalSwitchStatusComponent.getSwitchStatus()) { + String key = getSenderDispatchKeyForToday(); + cache.put(key + ":" + deviceId, cache.getUnchecked(key + ":" + deviceId) + 1); + // } + } + + public void incrementDailyAlertCount(String deviceId) { + // if (this.globalSwitchStatusComponent.getSwitchStatus()) { + String key = getSenderAlertKeyForToday(); + cache.put(key + ":" + deviceId, cache.getUnchecked(key + ":" + deviceId) + 1); + // } + } + + private String getKeyForToday() { + LocalDate today = LocalDate.now(); + ZoneId japanZone = ZoneId.of("Asia/Tokyo"); + ZonedDateTime nowInJapan = ZonedDateTime.now(japanZone); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + LocalDate yesterdayInJapan = nowInJapan.toLocalDate(); + return "sender:device:counts:" + formatter.format(yesterdayInJapan); + } + + private String getSenderDeviceKeyForToday() { + LocalDate today = LocalDate.now(); + return "sender:device:counts:" + today.format(DateTimeFormatter.ISO_LOCAL_DATE); + } + + private String getSenderBuildingKeyForToday() { + LocalDate today = LocalDate.now(); + return "sender:building:counts:" + today.format(DateTimeFormatter.ISO_LOCAL_DATE); + } + + private String getSenderDispatchKeyForToday() { + LocalDate today = LocalDate.now(); + return "sender:dispatch:counts:" + today.format(DateTimeFormatter.ISO_LOCAL_DATE); + } + + private String getSenderAlertKeyForToday() { + LocalDate today = LocalDate.now(); + return "sender:alert:counts:" + today.format(DateTimeFormatter.ISO_LOCAL_DATE); + } + + private String getSenderAlertCancelKeyForToday() { + LocalDate today = LocalDate.now(); + return "sender:alert_cancel:counts:" + today.format(DateTimeFormatter.ISO_LOCAL_DATE); + } + + + public void syncToRedis() { + String requestId = UUID.randomUUID().toString(); + MDC.put("requestId", requestId); + log.info("Syncing cache to Redis=============================>"); + log.info("Current Cache Size Data {}", cache.size()); + log.info("Current Cache Value :{}", JSON.toJSON(cache.asMap())); + cache.asMap().forEach((key, value) -> { + String[] parts = key.split(":"); + String redisKey = parts[0] + ":" + parts[1] + ":" + parts[2] + ":" + parts[3]; + String deviceId = parts[4]; + redisTemplate.opsForHash().increment(redisKey, deviceId, value); + redisTemplate.expire(redisKey, Duration.ofDays(2)); // 设置键2天后过期 + }); + log.info("Syncing cache to Redis done"); + this.cache.invalidateAll(); + MDC.clear(); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/components/MdcLogEnhancerFilter.java b/src/main/java/com/techsor/datacenter/sender/components/MdcLogEnhancerFilter.java new file mode 100644 index 0000000..4b6c315 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/components/MdcLogEnhancerFilter.java @@ -0,0 +1,30 @@ +package com.techsor.datacenter.sender.components; + +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.UUID; +import org.slf4j.MDC; + +@Component +public class MdcLogEnhancerFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String requestId = request.getHeader("X-Request-ID"); + if (requestId == null || requestId.isEmpty()) { + requestId = UUID.randomUUID().toString(); + } + MDC.put("requestId", requestId); + + try { + filterChain.doFilter(request, response); + } finally { + MDC.clear(); + } + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/components/SendMail.java b/src/main/java/com/techsor/datacenter/sender/components/SendMail.java new file mode 100644 index 0000000..bbbfc32 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/components/SendMail.java @@ -0,0 +1,228 @@ +package com.techsor.datacenter.sender.components; + +import com.alibaba.fastjson2.JSON; +import com.google.gson.Gson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.mail.*; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeUtility; +import javax.net.ssl.SSLSocketFactory; +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.util.Properties; + +/**** + * 发送邮件,调用邮箱API接口 + * + * **/ +@Component +public class SendMail { + private static Logger logger = LoggerFactory.getLogger(SendMail.class); + /** + * Message对象将存储我们实际发送的电子邮件信息, + * Message对象被作为一个MimeMessage对象来创建并且需要知道应当选择哪一个JavaMail session。 + */ + private MimeMessage message; + + /** + * Session类代表JavaMail中的一个邮件会话。 + * 每一个基于JavaMail的应用程序至少有一个Session(可以有任意多的Session)。 + * + * JavaMail需要Properties来创建一个session对象。 寻找"mail.smtp.host" 属性值就是发送邮件的主机 + * 寻找"mail.smtp.auth" 身份验证,目前免费邮件服务器都需要这一项 + */ + private Session session; + + + /*** + * 邮件是既可以被发送也可以被受到。JavaMail使用了两个不同的类来完成这两个功能:Transport 和 Store。 Transport + * 是用来发送信息的,而Store用来收信。对于我们只需要用到Transport对象。 + */ + private Transport transport; + + private String mailHost = "email-smtp.ap-northeast-1.amazonaws.com"; + private int mailPort = 465; + private String sender_username = "AKIAVRXFMB43Z4Q6WGZN"; + private String sender_password = "BL2KvrpRMDLPmyTqm2yqN9xGi5dQOAMwL/vX4aIrVm6X"; + private String email_sendername = "datacenter-info"; + private boolean mailSsl = true; + private boolean password_encrypted = false; + private String mail_from="alert@ttkdatatechbuild.com"; + + /** + * + * @param subject + * @param sendHtml + * @param receiveUser + */ + public void sendMail(String subject, String sendHtml, String receiveUser, String cc){ + if(this.mailSsl) { + logger.debug("begin to send mail ssl {},{}",subject,receiveUser); + sendMailSsl(subject, sendHtml, receiveUser, cc); + }else { + logger.debug("begin to send mail doSendHtmlEmail25 {},{}",subject,receiveUser); + doSendHtmlEmail25(subject, sendHtml, receiveUser, cc); + } + } + + /** + * 发送邮件 + * + * @param subject + * 邮件主题 + * @param sendHtml + * 邮件内容 + * @param receiveUser + * 收件人地址 + * @param cc + */ + public void doSendHtmlEmail25(String subject, String sendHtml, String receiveUser, String cc) { + try { + // 发件人 + // InternetAddress from = new InternetAddress(sender_username); + // 下面这个是设置发送人的Nick name + InternetAddress from = new InternetAddress( + MimeUtility.encodeWord(email_sendername) + " <" + mail_from + ">"); + message.setFrom(from); + + // 收件人 + InternetAddress to = new InternetAddress(receiveUser); + message.setRecipient(Message.RecipientType.TO, to);// 还可以有CC、BCC +// InternetAddress cto = new InternetAddress(mail_from); +// message.setRecipient(Message.RecipientType.CC, cto); + if (!StringUtils.isEmpty(cc)) { + message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(cc)); + } + + // 邮件主题 + message.setSubject(subject); + + String content = sendHtml.toString(); + // 邮件内容,也可以使纯文本"text/plain" + message.setContent(content, "text/html;charset=UTF-8"); + + // 保存邮件 + message.saveChanges(); + + transport = session.getTransport("smtp"); + // smtp验证,就是你用来发邮件的邮箱用户名密码 + transport.connect(mailHost, mailPort,sender_username, sender_password); + // 发送 + transport.sendMessage(message, message.getAllRecipients()); + // System.out.println("send success!"); + logger.info("send success"); + } catch (Exception e) { + logger.error("[SendMail Error] doSendHtmlEmail25出错", e); + } finally { + if (transport != null) { + try { + transport.close(); + } catch (MessagingException e) { + logger.error("[SendMail Error] doSendHtmlEmail25 transport出错", e); + } + } + } + } + + + public void sendMailSsl(String subject, String sendHtml, String receiveUser, String cc){ + try { + final String SSL_FACTORY = SSLSocketFactory.getDefault().getClass().getName(); + + final Properties p = System.getProperties() ; + p.setProperty("mail.smtp.host", mailHost); + p.setProperty("mail.smtp.auth", "true"); + p.setProperty("mail.smtp.ssl.protocols", "TLSv1.2"); + p.setProperty("mail.smtp.user", sender_username); + p.setProperty("mail.smtp.pass", sender_password); + + p.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY); + p.setProperty("mail.smtp.socketFactory.fallback", "false"); + //邮箱发送服务器端口,这里设置为465端口 + p.setProperty("mail.smtp.port", mailPort+""); + p.setProperty("mail.smtp.socketFactory.port",mailPort+""); + + // 根据邮件会话属性和密码验证器构造一个发送邮件的session + Session session = Session.getInstance(p, new Authenticator(){ + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(p.getProperty("mail.smtp.user"),p.getProperty("mail.smtp.pass")); + } + }); + session.setDebug(true); + Message message = new MimeMessage(session); + //消息发送的主题 + message.setSubject(subject); + //接受消息的人 + message.setReplyTo(InternetAddress.parse(mail_from)); + + //消息的发送者 + InternetAddress from = new InternetAddress( + MimeUtility.encodeWord(email_sendername) + " <" + mail_from + ">"); + message.setFrom(from); + +// message.setFrom(new InternetAddress(p.getProperty("mail.smtp.user"),sender_username)); + // 创建邮件的接收者地址,并设置到邮件消息中 +// String[] split = receiveUser.split(","); +// InternetAddress []tos = new InternetAddress[split.length]; +// for (int i = 0; i < split.length; i++) { +// tos[i]=new InternetAddress(split[i]); +// } + // 设置抄送人 +// if (cc != null && cc.length() > 0) { +// message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(cc)); +// } + //message.setRecipients(Message.RecipientType.TO, tos); + + InternetAddress to = new InternetAddress(receiveUser); + message.setRecipient(Message.RecipientType.TO, to);// 还可以有CC、BCC +// InternetAddress cto = new InternetAddress(mail_from); +// message.setRecipient(Message.RecipientType.CC, cto); + if (!StringUtils.isEmpty(cc)) { + message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(cc)); + } + + + // 消息发送的时间 + message.setSentDate(new Date()); + + String content = sendHtml.toString(); + // 邮件内容,也可以使纯文本"text/plain" + message.setContent(content, "text/html;charset=UTF-8"); + +// Multipart mainPart = new MimeMultipart(); +// // 创建一个包含HTML内容的MimeBodyPart +// BodyPart html = new MimeBodyPart(); +// // 设置HTML内容 +// html.setContent(sendHtml+ email_urlinfo, "text/html; charset=utf-8"); +// mainPart.addBodyPart(html); +// // 将MiniMultipart对象设置为邮件内容 +// message.setContent(mainPart); +// // 设置附件 +//// if (fileList != null && fileList.length > 0) { +//// for (int i = 0; i < fileList.length; i++) { +//// html = new MimeBodyPart(); +//// FileDataSource fds = new FileDataSource(fileList[i]); +//// html.setDataHandler(new DataHandler(fds)); +//// html.setFileName(MimeUtility.encodeText(fds.getName(), "UTF-8", "B")); +//// mainPart.addBodyPart(html); +//// } +//// } +// message.setContent(mainPart); + message.saveChanges(); + Transport.send(message); + logger.info("send success"); + } catch (MessagingException e) { + logger.error("[SendMail Error] sendMail--error:"+e.getMessage(),e); + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + logger.error("[SendMail Error] sendMail--error:"+e.getMessage(),e); + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/config/AsyncConfig.java b/src/main/java/com/techsor/datacenter/sender/config/AsyncConfig.java new file mode 100644 index 0000000..db5f084 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/AsyncConfig.java @@ -0,0 +1,9 @@ +package com.techsor.datacenter.sender.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; + +@Configuration +@EnableAsync +public class AsyncConfig { +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/config/DataCenterEnvConfig.java b/src/main/java/com/techsor/datacenter/sender/config/DataCenterEnvConfig.java new file mode 100644 index 0000000..6170dc5 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/DataCenterEnvConfig.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +public class DataCenterEnvConfig { + + @Value("${data.center.sender.url:#{'http://localhost:8033/nesic/deviceId/'}}") + private String jgUrl; + + +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/config/DataSourceAdminConfig.java b/src/main/java/com/techsor/datacenter/sender/config/DataSourceAdminConfig.java new file mode 100644 index 0000000..fc80069 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/DataSourceAdminConfig.java @@ -0,0 +1,141 @@ +package com.techsor.datacenter.sender.config; + + +import com.zaxxer.hikari.HikariDataSource; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class DataSourceAdminConfig { + + @Value("${spring.datasource.admin.name}") + private String name; + + @Value("${spring.datasource.admin.url}") + private String url; + + @Value("${spring.datasource.admin.username}") + private String username; + + @Value("${spring.datasource.admin.password}") + private String password; + + @Value("${spring.datasource.admin.driverClassName}") + private String driverClassName; + + + + @Value("${spring.datasource.admin.hikari.schema}") + private String schema; + + @Value("${spring.datasource.admin.hikari.minimum-idle}") + private Integer mininumIdle; + + @Value("${spring.datasource.admin.hikari.maximum-pool-size}") + private Integer maximuPoolSize; + + @Value("${spring.datasource.admin.hikari.connection-timeout}") + private Integer connectionTimeout; + + @Value("${dynamic.jdbc.url}") + private String dynamicJdbcUrl; + + /** + * 获取admin数据源 + * + * */ + @Primary + @Bean + public DataSource adminDatasource() { + HikariDataSource adminDatasource = DataSourceBuilder.create() + .url(url) + .username(username) + .password(password).driverClassName(driverClassName) + .type(HikariDataSource.class) + .build(); + adminDatasource.setSchema(schema); + adminDatasource.setMinimumIdle(mininumIdle); + adminDatasource.setMaximumPoolSize(maximuPoolSize); + adminDatasource.setConnectionTimeout(connectionTimeout); + return adminDatasource; + } + + +// @Bean +// public DataSource thirdDatasource() { +// String dbUrl=String.format(dynamicJdbcUrl,"data_center_third"); +// HikariDataSource adminDatasource = DataSourceBuilder.create() +// .url(dbUrl) +// .username(username) +// .password(password).driverClassName(driverClassName) +// .type(HikariDataSource.class) +// .build(); +// adminDatasource.setSchema("data_center_third"); +// adminDatasource.setMinimumIdle(mininumIdle); +// adminDatasource.setMaximumPoolSize(maximuPoolSize); +// adminDatasource.setConnectionTimeout(connectionTimeout); +// return adminDatasource; +// } + + /** + * 构造jdbcTemplate,用于获取动态数据源 + * **/ + @Bean + public JdbcTemplate adminJdbcTemplate(@Qualifier("adminDatasource") DataSource adminDatasource) { + return new JdbcTemplate(adminDatasource); + } + + + + @Bean + public DataSource dynamicDataSource(JdbcTemplate adminJdbcTemplate,@Qualifier("adminDatasource") DataSource adminDatasource + // ,@Qualifier("thirdDatasource") DataSource thirdDatasource + ) { + DynamicRouteDataSource dynamicDataSource = new DynamicRouteDataSource(); + Map targetDataSources = new HashMap<>(); + + String sql="\t\tSELECT\n" + + "\t\t\tbcom.id,\n" + + "\t\t\tbcom.company_name companyName\n" + + "\t\tFROM\n" + + "\t\t\tdata_center_aeon_admin.basic_company bcom\n" + + "\t\tWHERE bcom.flag != 1"; + + adminJdbcTemplate.query(sql,rs->{ + HikariDataSource dataSource1 = new HikariDataSource(); + String dbName="data_center_aeon_"+rs.getInt("id"); + String dbUrl=String.format(dynamicJdbcUrl,dbName); + dataSource1.setJdbcUrl(dbUrl); + dataSource1.setUsername(username); + dataSource1.setPassword(password); + dataSource1.setDriverClassName(driverClassName); + dataSource1.setSchema(dbName); + dataSource1.setMinimumIdle(mininumIdle); + dataSource1.setMaximumPoolSize(maximuPoolSize); + dataSource1.setConnectionTimeout(connectionTimeout); + targetDataSources.put("dataSourceForCompany_"+rs.getInt("id"), dataSource1); + }); + // targetDataSources.put("dataSourceForCompany_0", thirdDatasource); + + dynamicDataSource.setTargetDataSources(targetDataSources); + dynamicDataSource.setDefaultTargetDataSource(adminDatasource); // 设置默认数据源 + return dynamicDataSource; + } + + + @Bean + public JdbcTemplate jdbcTemplate(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) { + return new JdbcTemplate(dynamicDataSource); + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/config/DataSourceContextHolder.java b/src/main/java/com/techsor/datacenter/sender/config/DataSourceContextHolder.java new file mode 100644 index 0000000..49502e7 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/DataSourceContextHolder.java @@ -0,0 +1,18 @@ +package com.techsor.datacenter.sender.config; + +public class DataSourceContextHolder { + + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static void setCurrentDataSourceKey(String dataSourceKey) { + contextHolder.set(dataSourceKey); + } + + public static String getCurrentDataSourceKey() { + return contextHolder.get(); + } + + public static void clearCurrentDataSourceKey() { + contextHolder.remove(); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/config/DynamicRouteDataSource.java b/src/main/java/com/techsor/datacenter/sender/config/DynamicRouteDataSource.java new file mode 100644 index 0000000..995e4d0 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/DynamicRouteDataSource.java @@ -0,0 +1,12 @@ +package com.techsor.datacenter.sender.config; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + + +public class DynamicRouteDataSource extends AbstractRoutingDataSource { + + @Override + protected Object determineCurrentLookupKey() { + return DataSourceContextHolder.getCurrentDataSourceKey(); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/config/GlobalExceptionHandler.java b/src/main/java/com/techsor/datacenter/sender/config/GlobalExceptionHandler.java new file mode 100644 index 0000000..bbe2289 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/GlobalExceptionHandler.java @@ -0,0 +1,17 @@ +package com.techsor.datacenter.sender.config; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.OK); + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/config/IotCoreConfiguration.java b/src/main/java/com/techsor/datacenter/sender/config/IotCoreConfiguration.java new file mode 100644 index 0000000..6bf70c7 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/IotCoreConfiguration.java @@ -0,0 +1,167 @@ +package com.techsor.datacenter.sender.config; + +import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.eclipse.paho.client.mqttv3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +import lombok.extern.slf4j.Slf4j; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.stream.Collectors; + +@Configuration +public class IotCoreConfiguration { + + private Logger logger = LoggerFactory.getLogger(IotCoreConfiguration.class); + + @Value("${iotcore.endpoint}") + private String iotcoreEndpoint; + + @Value("${iotcore.port}") + private Integer iotcorePort ; + + @Value("${iotcore.clientId}") + private String iotcoreClientId; + + @Value("${iotcore.env}") + private String iotcoreEnv; + + + @Bean + public MqttClient mqttClient() throws Exception { + + String brokerUrl = "ssl://" + iotcoreEndpoint + ":" + iotcorePort; + String clientId = iotcoreClientId+ "-" + System.currentTimeMillis(); + +// String caCertFile = "C:\\Users\\jwy\\Desktop\\TMP\\iotcore\\root-CA.crt"; +// String clientCertFile = "C:\\Users\\jwy\\Desktop\\TMP\\iotcore\\tkbuild-thing.cert.pem"; +// String privateKeyFile = "C:\\Users\\jwy\\Desktop\\TMP\\iotcore\\tkbuild-thing.private.key"; + + MqttClient client = null; + try { + + logger.info("iotcoreEndpoint:{}", iotcoreEndpoint); + logger.info("iotcorePort:{}", iotcorePort); + logger.info("iotcoreClientId:{}", clientId); + + // 加载 BouncyCastle 提供器 + Security.addProvider(new BouncyCastleProvider()); + + // 创建 KeyStore 并加载 CA 证书 + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, null); + + // 加载 CA 证书 + logger.info("load iotcore.ca.crt......"); + InputStream caCertStream = IotCoreConfiguration.class.getResourceAsStream("/ssl/" + iotcoreEnv +"/iotcore.root.pem"); + X509Certificate caCert = (X509Certificate) cf.generateCertificate(caCertStream); + keyStore.setCertificateEntry("ca-cert", caCert); + + + // 加载客户端证书 + logger.info("load iotcore.cert.pem......"); + InputStream clientCertStream = IotCoreConfiguration.class.getResourceAsStream("/ssl/" + iotcoreEnv +"/iotcore.cert.pem"); + X509Certificate clientCert = (X509Certificate) cf.generateCertificate(clientCertStream); + keyStore.setCertificateEntry("client-cert", clientCert); + + // 加载私钥 + logger.info("load iotcore.private.key......"); + PrivateKey privateKey = loadPrivateKey("/ssl/" + iotcoreEnv +"/iotcore.private.key"); + keyStore.setKeyEntry("client-key", privateKey, "".toCharArray(), new java.security.cert.Certificate[]{clientCert}); + + + // 设置 KeyManager 和 TrustManager + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, "".toCharArray()); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(keyStore); + + SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + // 创建 MQTT 客户端 + MqttConnectOptions options = new MqttConnectOptions(); + options.setSocketFactory(sslContext.getSocketFactory()); + options.setCleanSession(false); + options.setAutomaticReconnect(true); + options.setKeepAliveInterval(30); + + client = new MqttClient(brokerUrl, clientId); + // 设置回调函数 + client.setCallback(new MqttCallback() { + @Override + public void connectionLost(Throwable cause) { + logger.error("Connection lost: " + cause.getMessage()); + // 自动重连功能会由 MqttClient 自动处理 + } + + @Override + public void messageArrived(String topic, MqttMessage message) throws Exception { + } + + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + logger.info("IotCoreConfiguration Delivery complete: " + token.getMessageId()); + } + }); + client.connect(options); + + logger.info("Connected to AWS IoT Core......"); + } catch (Exception e) { + logger.error("Connect to AWS IoT Core error", e); + } + + if(null == client) { + logger.error("client is null............"); + } + + return client; + } + + private PrivateKey loadPrivateKey(String filePath) throws Exception { + InputStream inputStream = IotCoreConfiguration.class.getResourceAsStream(filePath); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + // 过滤掉以 "-----" 开头的行,并合并成单一字符串 + String key = reader.lines() + .filter(line -> !line.startsWith("-----")) + .collect(Collectors.joining()); + + + byte[] keyBytes = Base64.getDecoder().decode(key); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePrivate(spec); + } +} + diff --git a/src/main/java/com/techsor/datacenter/sender/config/JdbcTemplateConfig.java b/src/main/java/com/techsor/datacenter/sender/config/JdbcTemplateConfig.java new file mode 100644 index 0000000..b6dffe7 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/JdbcTemplateConfig.java @@ -0,0 +1,16 @@ +//package com.techsor.datacenter.sender.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.jdbc.core.JdbcTemplate; +// +//import javax.sql.DataSource; +// +//@Configuration +//public class JdbcTemplateConfig { +// +// @Bean +// public JdbcTemplate jdbcTemplate(DataSource dataSource){ +// return new JdbcTemplate(dataSource); +// } +//} diff --git a/src/main/java/com/techsor/datacenter/sender/config/KinesisAutoConfig.java b/src/main/java/com/techsor/datacenter/sender/config/KinesisAutoConfig.java new file mode 100644 index 0000000..efb3d75 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/KinesisAutoConfig.java @@ -0,0 +1,60 @@ +package com.techsor.datacenter.sender.config; + +import com.amazonaws.ClientConfiguration; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.kinesis.AmazonKinesis; +import com.amazonaws.services.kinesis.AmazonKinesisClientBuilder; +import com.amazonaws.services.kinesis.producer.KinesisProducer; +import com.amazonaws.services.kinesis.producer.KinesisProducerConfiguration; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static software.amazon.awssdk.regions.Region.AP_NORTHEAST_1; + + + +@Configuration +public class KinesisAutoConfig { + + @Value("${amazon.aws.accesskey}") + private String amazonAWSAccessKey; + + @Value("${amazon.aws.secretkey}") + private String amazonAWSSecretKey; + + @Bean + public AWSCredentialsProvider amazonAWSCredentialsProvider(AWSCredentials amazonAWSCredentials) { + return new AWSStaticCredentialsProvider(amazonAWSCredentials); + } + + @Bean + public AWSCredentials amazonAWSCredentials() { + return new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey); + } + + @Bean + public AmazonKinesis kinesisConfig(AWSCredentials amazonAWSCredentials) { + ClientConfiguration clientConfiguration=new ClientConfiguration(); + clientConfiguration.setMaxErrorRetry(3); + clientConfiguration.setRequestTimeout(100000); + clientConfiguration.setMaxConnections(100000); + clientConfiguration.setUseTcpKeepAlive(true); + clientConfiguration.setUseGzip(false); + clientConfiguration.setClientExecutionTimeout(10000); + clientConfiguration.setConnectionMaxIdleMillis(100000); + + return AmazonKinesisClientBuilder.standard() + .withClientConfiguration(clientConfiguration) + .withCredentials(new AWSStaticCredentialsProvider(amazonAWSCredentials)) + .withRegion(Regions.AP_NORTHEAST_1) // 选择适合你的区域 + .build(); + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/config/LettuceRedisConfig.java b/src/main/java/com/techsor/datacenter/sender/config/LettuceRedisConfig.java new file mode 100644 index 0000000..4a20031 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/LettuceRedisConfig.java @@ -0,0 +1,194 @@ +package com.techsor.datacenter.sender.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; +import com.techsor.datacenter.sender.listener.RedisNotificationMessageSubscriber; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Scope; +import org.springframework.core.env.Environment; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.PatternTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.integration.redis.util.RedisLockRegistry; + +@Configuration +public class LettuceRedisConfig { + + + + @Value("${alarm.redis.host}") + private String REDIS_HOST; + + @Value("${alarm.redis.port}") + private Integer REDIS_PORT; + + @Value("${alarm.redis.password}") + private String REDIS_PASSWORD; + + @Value("${alarm.redis.database}") + private Integer REDIS_DATABASE; + + + + @Value("${spring.redis.database}") + private Integer SPRING_REDIS_DATABASE; + + + @Bean("redisConnectionFactory") + public RedisConnectionFactory redisConnectionFactory(){ + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(REDIS_HOST); + redisStandaloneConfiguration.setPort(REDIS_PORT); + if (REDIS_PASSWORD != null && !REDIS_PASSWORD.isEmpty()) { + redisStandaloneConfiguration.setPassword(REDIS_PASSWORD); + } + redisStandaloneConfiguration.setDatabase(SPRING_REDIS_DATABASE); + LettucePoolingClientConfiguration lettucePoolingClientConfiguration = LettucePoolingClientConfiguration.builder() + .poolConfig(new GenericObjectPoolConfig()) + .build(); + LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration, lettucePoolingClientConfiguration); + return lettuceConnectionFactory; + } + + + + + @Bean("alarmRedisConnectionFactory") + public RedisConnectionFactory alarmRedisConnectionFactory(){ + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(REDIS_HOST); + redisStandaloneConfiguration.setPort(REDIS_PORT); + if (REDIS_PASSWORD != null && !REDIS_PASSWORD.isEmpty()) { + redisStandaloneConfiguration.setPassword(REDIS_PASSWORD); + } + redisStandaloneConfiguration.setDatabase(REDIS_DATABASE); + LettucePoolingClientConfiguration lettucePoolingClientConfiguration = LettucePoolingClientConfiguration.builder() + .poolConfig(new GenericObjectPoolConfig()) + .build(); + LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration, lettucePoolingClientConfiguration); + return lettuceConnectionFactory; + } + + + + @Bean("alramRedisTemplate") + public RedisTemplate alramRedisTemplate(@Qualifier("alarmRedisConnectionFactory") RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = getRedisTemplate(redisConnectionFactory); + return redisTemplate; + } + @Bean("redisTemplate") + public RedisTemplate redisTemplate(@Qualifier("redisConnectionFactory") RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = getRedisTemplate(redisConnectionFactory); + return redisTemplate; + } + + @Bean + public HashOperations hashOperations(@Qualifier("alramRedisTemplate") RedisTemplate alramRedisTemplate) { + return alramRedisTemplate.opsForHash(); + } + + @Bean("stringRedisTemplate") + public RedisTemplate stringRedisTemplate(@Qualifier("redisConnectionFactory") RedisConnectionFactory redisConnectionFactory) { + return getStringRedisTemplate(redisConnectionFactory); + } + + public RedisTemplate getStringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + RedisSerializer stringSerializer = new StringRedisSerializer(); + redisTemplate.setKeySerializer(stringSerializer); + redisTemplate.setHashKeySerializer(stringSerializer); + + // 设置序列化接口 + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE); + mapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE); + SimpleFilterProvider filterProvider = new SimpleFilterProvider(); + filterProvider.setDefaultFilter(SimpleBeanPropertyFilter.serializeAll()); + mapper.setFilterProvider(filterProvider); + GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(mapper); + + // key 序列化 + redisTemplate.setKeySerializer(stringSerializer); + // value 序列化 + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); + // Hash key 序列化 + redisTemplate.setHashKeySerializer(stringSerializer); + // Hash value 序列化 + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); + + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + public RedisTemplate getRedisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + RedisSerializer stringSerializer = new StringRedisSerializer(); + redisTemplate.setKeySerializer(stringSerializer); + redisTemplate.setHashKeySerializer(stringSerializer); + //设置序列化接口 + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE); + mapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE); + SimpleFilterProvider filterProvider = new SimpleFilterProvider(); + filterProvider.setDefaultFilter(SimpleBeanPropertyFilter.serializeAll()); + mapper.setFilterProvider(filterProvider); + GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(mapper); + // key序列化 + redisTemplate.setKeySerializer(stringSerializer); + // value序列化 + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); + // Hash key序列化 + redisTemplate.setHashKeySerializer(stringSerializer); + // Hash value序列化 + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + + @Value("${redis.lock.expire}") + private Integer lockAttempTimeout ; + /*** + * 定义redis分布式锁 + * **/ + @Bean + public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory){ + return new RedisLockRegistry(redisConnectionFactory, "redis-lock",lockAttempTimeout); + } + + @Bean + RedisMessageListenerContainer container( @Qualifier("redisConnectionFactory") RedisConnectionFactory connectionFactory, + MessageListenerAdapter listenerAdapter) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + container.addMessageListener(listenerAdapter, new PatternTopic("notificationReceiver")); + return container; + } + + @Bean + MessageListenerAdapter listenerAdapter(RedisNotificationMessageSubscriber subscriber) { + return new MessageListenerAdapter(subscriber); + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/config/RestTemplateConfig.java b/src/main/java/com/techsor/datacenter/sender/config/RestTemplateConfig.java new file mode 100644 index 0000000..a07dabb --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/RestTemplateConfig.java @@ -0,0 +1,66 @@ +package com.techsor.datacenter.sender.config; + + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import java.util.concurrent.TimeUnit; + +/*** + * 使用okhttp connectionFactory + * **/ +@Configuration +public class RestTemplateConfig { + + + @Value("${ok.http.connect-timeout}") + private Integer connectTimeout; + + @Value("${ok.http.read-timeout}") + private Integer readTimeout; + + @Value("${ok.http.write-timeout}") + private Integer writeTimeout; + + @Value("${ok.http.max-idle-connections}") + private Integer maxIdleConnections; + + @Value("${ok.http.keep-alive-duration}") + private Long keepAliveDuration; + + + /** + * 声明 RestTemplate + */ + @Bean + public RestTemplate httpRestTemplate() { + ClientHttpRequestFactory factory = httpRequestFactory(); + RestTemplate restTemplate = new RestTemplate(factory); + return restTemplate; + } + + public ClientHttpRequestFactory httpRequestFactory() { + return new OkHttp3ClientHttpRequestFactory(okHttpConfigClient()); + } + + public OkHttpClient okHttpConfigClient(){ + return new OkHttpClient().newBuilder() + .connectionPool(pool()) + .connectTimeout(connectTimeout, TimeUnit.SECONDS) + .readTimeout(readTimeout, TimeUnit.SECONDS) + .writeTimeout(writeTimeout, TimeUnit.SECONDS) + .hostnameVerifier((hostname, session) -> true) + .build(); + } + + public ConnectionPool pool() { + return new ConnectionPool(maxIdleConnections, keepAliveDuration, TimeUnit.SECONDS); + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/config/SysMqttConfiguration.java b/src/main/java/com/techsor/datacenter/sender/config/SysMqttConfiguration.java new file mode 100644 index 0000000..c630935 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/SysMqttConfiguration.java @@ -0,0 +1,80 @@ +package com.techsor.datacenter.sender.config; + +import org.eclipse.paho.client.mqttv3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +//@Configuration +public class SysMqttConfiguration { + + private static final Logger log = LoggerFactory.getLogger(SysMqttConfiguration.class); + + @Value("${sys.mqtt.endpoint}") + private String sysMqttEndpoint; + + @Value("${sys.mqtt.port}") + private int sysMqttPort; + + @Value("${sys.mqtt.username}") + private String sysMqttUsername; + + @Value("${sys.mqtt.password}") + private String sysMqttPassword; + + @Value("${sys.mqtt.clientId}") + private String sysMqttClientId; + + @Bean(name = "sysMqttClient") + public MqttClient sysMqttClient() { + try { + String brokerUrl = "tcp://" + sysMqttEndpoint + ":" + sysMqttPort; + String clientId = sysMqttClientId + "-" + System.currentTimeMillis(); + + log.info("Initializing SYS MQTT client: {}", clientId); + log.info("Broker: {}", brokerUrl); + + MqttClient client = new MqttClient(brokerUrl, clientId, null); + + MqttConnectOptions options = new MqttConnectOptions(); + options.setCleanSession(true); + options.setAutomaticReconnect(true); + options.setKeepAliveInterval(30); + + if (sysMqttUsername != null && !sysMqttUsername.isEmpty()) { + options.setUserName(sysMqttUsername); + } + if (sysMqttPassword != null && !sysMqttPassword.isEmpty()) { + options.setPassword(sysMqttPassword.toCharArray()); + } + + client.setCallback(new MqttCallback() { + @Override + public void connectionLost(Throwable cause) { + log.warn("SYS MQTT connection lost: {}", cause.getMessage()); + } + + @Override + public void messageArrived(String topic, MqttMessage message) { + log.info("SYS MQTT message arrived. Topic: {}, Message: {}", topic, new String(message.getPayload())); + } + + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + log.info("SYS MQTT delivery complete: {}", token.getMessageId()); + } + }); + + client.connect(options); + log.info("SYS MQTT connected successfully....."); + + return client; + + } catch (Exception e) { + log.error("Failed to initialize SYS MQTT client", e); + return null; + } + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/config/UidConfiguration.java b/src/main/java/com/techsor/datacenter/sender/config/UidConfiguration.java new file mode 100644 index 0000000..23907bb --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/UidConfiguration.java @@ -0,0 +1,32 @@ +package com.techsor.datacenter.sender.config; + +import com.baidu.fsg.uid.UidGenerator; +import com.baidu.fsg.uid.impl.DefaultUidGenerator; +import com.baidu.fsg.uid.worker.DisposableWorkerIdAssigner; +import com.baidu.fsg.uid.worker.WorkerIdAssigner; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties(UidProperties.class) +@ConditionalOnProperty(prefix = "uid", value = "enable", matchIfMissing = true) +public class UidConfiguration { + + @ConditionalOnProperty(prefix = "uid", name = "enableCache", havingValue = "true") + @Bean + public UidGenerator cacheUidGenerator() { + return new DefaultUidGenerator(); + } + + @ConditionalOnProperty(matchIfMissing = true, prefix = "uid", name = "enableCache", havingValue = "false") + @Bean + public UidGenerator uidGenerator() { + return new DefaultUidGenerator(); + } + @Bean + public WorkerIdAssigner workerIdAssigner(){ + return new DisposableWorkerIdAssigner(); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/config/UidProperties.java b/src/main/java/com/techsor/datacenter/sender/config/UidProperties.java new file mode 100644 index 0000000..70a062c --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/config/UidProperties.java @@ -0,0 +1,52 @@ +package com.techsor.datacenter.sender.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "uid") +public class UidProperties { + private boolean enable; + private boolean enableCache; + private int timeBits = 31; + private int workerBits = 17; + private int seqBits = 15; + + public int getTimeBits() { + return timeBits; + } + + public void setTimeBits(int timeBits) { + this.timeBits = timeBits; + } + + public int getWorkerBits() { + return workerBits; + } + + public void setWorkerBits(int workerBits) { + this.workerBits = workerBits; + } + + public int getSeqBits() { + return seqBits; + } + + public void setSeqBits(int seqBits) { + this.seqBits = seqBits; + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public boolean isEnableCache() { + return enableCache; + } + + public void setEnableCache(boolean enableCache) { + this.enableCache = enableCache; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/constants/CompanyConstants.java b/src/main/java/com/techsor/datacenter/sender/constants/CompanyConstants.java new file mode 100644 index 0000000..1a110ca --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/constants/CompanyConstants.java @@ -0,0 +1,12 @@ +package com.techsor.datacenter.sender.constants; + +public class CompanyConstants { + public final static String ZIFISENSE="zifisense"; + public final static String NBI="nbi"; + public final static String OVIPHONE="oviphone"; + public final static String NITTAN="nittan"; + public final static String OCR="ocr"; + public final static String DELTA="delta"; + public final static String METCOM="metcom"; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/constants/Constants.java b/src/main/java/com/techsor/datacenter/sender/constants/Constants.java new file mode 100644 index 0000000..bf6d16c --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/constants/Constants.java @@ -0,0 +1,52 @@ +package com.techsor.datacenter.sender.constants; + + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class Constants { + + public static final String REDIS_DEVICE_ALARM_KEY = "lambda_device_alarm"; + public static final String REDIS_DEVICE_CANCEL_ALARM_KEY = "device_cancel_alarm"; + public static final String STATUS_ALARM = "alert"; + public static final String STATUS_ALERT_CANCEL = "alert_cancel"; + + public static final String ALERT_CANCEL_INFO = "【%s アラートメール】%s %s の%s解除を検知しました"; + public static final String ALERT_INFO = "【%s アラートメール】%s %s で%sしました"; + + +// public static Logger logger = LoggerFactory.getLogger(AlarmDataPush.class); +// //"http://tokyo-build-web-alb-481573367.ap-northeast-1.elb.amazonaws.com:20008/api/targetConfig/config/queryByDeviceId?deviceId="; +// public static final String QUERY_PUSH_INFO = "http://tokyo-build-prod-alb-2098411302.ap-northeast-1.elb.amazonaws.com:20008/targetConfig/config/v1/queryAlertForwardConfigByDeviceId?deviceId="; +// //"http://47.91.16.51:8080/rawJson/post/metcom" +//// public static final String PUSH_URL_METCOM = "http://43.163.239.12:8030/nesic/deviceId/metcom"; +// //"http://47.91.16.51:8080/healthCheck" +//// public static final String HEALTHCHECK_URL = "http://47.91.16.51:8080/healthCheck"; + + @Value("${query.push.info:}") + public String QUERY_PUSH_INFO; + + @Value("${roid.alarm.url:}") + public String ROID_ALARM_URL; + + @Value("${roid.alarm.cancel.url:}") + public String ROID_ALARM_CANCEL_URL; + + @Value("${roid.authorization:}") + public String ROID_AUTHORIZATION; + + @Value("${alarm.receiver:}") + public String ALARM_RECEIVER ; + + @Value("${email.restful.url:}") + public String EMAIL_RESULT_URL; + + + public static final String SWITCH_STATUS_PREFIX="switch:stats:status"; + + public static final String STATISTICS_ACCUMULATE_LATEST_PREFIX = "statistics:accumulate:latest_value:"; + public static final String STATISTICS_MEASURE_LATEST_PREFIX = "statistics:measure:latest_value:"; + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/constants/DeviceTypeConstants.java b/src/main/java/com/techsor/datacenter/sender/constants/DeviceTypeConstants.java new file mode 100644 index 0000000..687ea3e --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/constants/DeviceTypeConstants.java @@ -0,0 +1,6 @@ +package com.techsor.datacenter.sender.constants; + +public class DeviceTypeConstants { + public final static int normal=40; // 普通数据 + public final static int difference=43; // 差值数据 +} diff --git a/src/main/java/com/techsor/datacenter/sender/constants/MsgConstants.java b/src/main/java/com/techsor/datacenter/sender/constants/MsgConstants.java new file mode 100644 index 0000000..5d050d4 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/constants/MsgConstants.java @@ -0,0 +1,19 @@ +package com.techsor.datacenter.sender.constants; + +public class MsgConstants { + + public static String MSG_SERVER_ERROR="服务器问题,请联系管理员"; + public static String MSG_WRONG_VERIFYCODE="验证码错误"; + public static String MSG_USER_NOT_REGISTERED="用户未注册,请注册"; + + + public static String REDIS_COMMAND_NOTIFICATION_TYPE="notification"; + + + public static String REIDS_COMMAND_SWITCH_TYPE="switch"; + + public static final String SWITCH_STATUS_PREFIX="switch:stats:status"; + + + public static final String REDIS_COMMAND_STAT_EVENT="startStat"; +} diff --git a/src/main/java/com/techsor/datacenter/sender/constants/TypeConstants.java b/src/main/java/com/techsor/datacenter/sender/constants/TypeConstants.java new file mode 100644 index 0000000..0d72aeb --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/constants/TypeConstants.java @@ -0,0 +1,9 @@ +package com.techsor.datacenter.sender.constants; + +/** + * Store some special type's id. + * Notice: These types id should be fixed. Cannot be changed. + */ +public class TypeConstants { + public final static Integer KINGIOSERVER = 79; +} diff --git a/src/main/java/com/techsor/datacenter/sender/controllers/HealthController.java b/src/main/java/com/techsor/datacenter/sender/controllers/HealthController.java new file mode 100644 index 0000000..c246acd --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/controllers/HealthController.java @@ -0,0 +1,42 @@ +package com.techsor.datacenter.sender.controllers; + +import com.alibaba.fastjson2.JSON; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +@RestController +public class HealthController { + + @GetMapping("/healthcheck") + public String healhealthcheck(){ + return "ok"; + } + + + @GetMapping("/health") + public String health(){ + RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + long startTime = runtimeMXBean.getStartTime(); + Map resultMap=new HashMap<>(); + resultMap.put("status","UP"); + resultMap.put("startUpDate",String.valueOf(startTime)); + return JSON.toJSONString(resultMap); + } + @GetMapping("/version") + public String getVersion() throws IOException { + Properties properties = new Properties(); + Resource currentResource = new ClassPathResource("git.properties"); + properties.load(currentResource.getInputStream()); + String version=properties.getProperty("git.commit.id.abbrev"); + return version; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/controllers/MainReceiverController.java b/src/main/java/com/techsor/datacenter/sender/controllers/MainReceiverController.java new file mode 100644 index 0000000..eab110b --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/controllers/MainReceiverController.java @@ -0,0 +1,308 @@ +package com.techsor.datacenter.sender.controllers; + +import com.google.gson.Gson; +import com.techsor.datacenter.sender.components.CommonOpt; +import com.techsor.datacenter.sender.config.DataSourceContextHolder; +import com.techsor.datacenter.sender.constants.CompanyConstants; +import com.techsor.datacenter.sender.entitiy.*; +import com.techsor.datacenter.sender.entitiy.zaiot.ProcessZAIoTRAWEntity; +import com.techsor.datacenter.sender.processers.*; +import com.techsor.datacenter.sender.service.IDataProcessService; +import com.techsor.datacenter.sender.service.KingIOServerService; +import com.techsor.datacenter.sender.service.St150Service; +import com.techsor.datacenter.sender.service.ZAIoTInnerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; +import java.text.ParseException; + +/* +* 基础接收控制器,从接收组件转发来的数据都先进入这里 +* */ +@RestController +@RequestMapping("v1/") +public class MainReceiverController { + private static final Logger logger = LoggerFactory.getLogger(MainReceiverController.class); + @Resource + DeltaProcessor deltaIProcessor; + @Resource + MetcomProcessor metcomProcessor; + + @Resource + ZETAProcessor zetaProcessor; + + @Resource + NBIProcessor nbiProcessor; + + @Resource + OviphoneProcessor oviphoneProcessor; + + @Resource + NittanProcessor nittanProcessor; + + @Resource + OCRProcessor ocrProcessor; + + @Resource + KingIOServerService kingIOServerService; + + @Resource + St150Service st150Service; + + @Resource + ZAIoTInnerService zaIoTInnerService; + + @Autowired + private AsyncDataProcessor asyncDataProcessor; + + @Resource + private DuplicateDataProcessor duplicateDataProcessor; + + @Resource + private CommonOpt commonOpt; + + @Resource + private IDataProcessService dataProcessService; + + @RequestMapping(value = "generic/process", method = RequestMethod.POST) + public JsonResponse processReceiver(@RequestBody ProcessRAWEntity rawEntity){ + logger.warn("DataSrcCode:{},Content:{}",new Object[]{rawEntity.getDataSrcCode(),rawEntity.getContent()}); + + boolean flag=this.duplicateDataProcessor.removeDuplicateData(rawEntity.getContent()); + + if (!flag){ + return JsonResponse.buildSuccess("success"); + } + logger.debug("After Duplicate =====>DataSrcCode:{},Content:{}",new Object[]{rawEntity.getDataSrcCode(),rawEntity.getContent()}); + String deviceId=rawEntity.getDataSrcCode(); + //处理数据,转发数据 + String resultJson=""; + try{ + resultJson=this.dataProcessService.processData(rawEntity.getContent(),deviceId); + }catch (Exception e){ + logger.error("Process data error:{}",e.getMessage()); + } + return JsonResponse.buildSuccess(resultJson); + } + + /** + * Special process for ioserver + * @param rawEntity + * @return + */ + @RequestMapping(value = "generic/ioserver_process", method = RequestMethod.POST) + public JsonResponse ioserverProcess(@RequestBody ProcessRAWEntity rawEntity){ + logger.warn("DataSrcCode:{},Content:{}",new Object[]{rawEntity.getDataSrcCode(),rawEntity.getContent()}); + + boolean flag=this.duplicateDataProcessor.removeDuplicateData(rawEntity.getContent()); + + if (!flag){ + return JsonResponse.buildSuccess("success"); + } + logger.debug("After Duplicate =====>DataSrcCode:{},Content:{}",new Object[]{rawEntity.getDataSrcCode(),rawEntity.getContent()}); + //处理数据,转发数据 + // 异步执行耗时任务 + asyncDataProcessor.processKingIOServerAsync(rawEntity.getContent(), kingIOServerService, dataProcessService); +// String resultJson=""; +// try{ +// //解析IOServer数据 +// String processJson=this.kingIOServerService.start(rawEntity.getContent()); +// dataProcessService.processKingIOServerData(processJson); +// +// }catch (Exception e){ +// logger.error("Process data error:{}",e.getMessage()); +// logger.error(e.getMessage(),e); +// } + return JsonResponse.buildSuccess("received"); + } + + /** + * Special process for ZT-150 Gateway + * @param rawEntity + * @return + */ + @RequestMapping(value = "generic/st150_process", method = RequestMethod.POST) + public JsonResponse st150Process(@RequestBody ProcessRAWEntity rawEntity){ + logger.warn("DataSrcCode:{},Content:{}",new Object[]{rawEntity.getDataSrcCode(),rawEntity.getContent()}); + + boolean flag=this.duplicateDataProcessor.removeDuplicateData(rawEntity.getContent()); + + if (!flag){ + return JsonResponse.buildSuccess("success"); + } + logger.debug("After Duplicate =====>DataSrcCode:{},Content:{}",new Object[]{rawEntity.getDataSrcCode(),rawEntity.getContent()}); + //处理数据,转发数据 + // 异步执行耗时任务 + asyncDataProcessor.processST150Async(rawEntity.getContent(), st150Service, dataProcessService); +// String resultJson=""; +// try{ +// //解析IOServer数据 +// String processJson=this.kingIOServerService.start(rawEntity.getContent()); +// dataProcessService.processKingIOServerData(processJson); +// +// }catch (Exception e){ +// logger.error("Process data error:{}",e.getMessage()); +// logger.error(e.getMessage(),e); +// } + return JsonResponse.buildSuccess("received"); + } + + /** + * Special process for ZETA zaiot sensor/振動センサー + * Since 振動センサー has two different device type to process. + * @param rawEntity + * @return + */ + @RequestMapping(value = "generic/zaiot_process", method = RequestMethod.POST) + public JsonResponse zaiotProcess(@RequestBody ProcessZAIoTRAWEntity rawEntity){ + logger.warn("DataSrcCode:{},Content:{}",new Object[]{rawEntity.getDeviceId(),rawEntity.getContent()}); + + logger.debug("After Duplicate =====>DataSrcCode:{},Content:{}",new Object[]{rawEntity.getDeviceId(),rawEntity.getContent()}); + //处理数据,转发数据 + String resultJson=""; + try{ + //解析振動センサー数据 + ResultDeviceIdEntity resultDeviceIdEntity=this.zaIoTInnerService.start(rawEntity.getContent(),rawEntity.getIdentify()); + if (resultDeviceIdEntity!=null){ + dataProcessService.processZAIoTData(resultDeviceIdEntity); + } + + }catch (Exception e){ + logger.error("Process data error:{}",e.getMessage()); + logger.error(e.getMessage(),e); + } + return JsonResponse.buildSuccess(resultJson); + } + + @RequestMapping(value = "generic/mockJsonData", method = RequestMethod.POST) + public JsonResponse mockJsonData(@RequestBody ProcessRAWEntity rawEntity){ + logger.warn("Received: "+new Gson().toJson(rawEntity)); + String deviceId=rawEntity.getDataSrcCode(); + //处理数据,转发数据 + String resultJson=""; + try{ + resultJson=this.dataProcessService.mockJsonData(rawEntity.getContent(),deviceId); + }catch (Exception e){ + logger.error("Process data error:{}",e.getMessage()); + } + return JsonResponse.buildSuccess(resultJson); + } + + @RequestMapping(value = "generic/mockMinuteLevelStorage", method = RequestMethod.POST) + public JsonResponse mockMinuteLevelStorage(@RequestBody DynamodbEntity rawEntity){ + logger.warn("mockMinuteLevelStorage Received: "+new Gson().toJson(rawEntity)); + //处理数据 + String resultJson=""; + try{ + String topCompanyId= commonOpt.getTopCompanyId(rawEntity.getDeviceId()); + //Switch database + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + this.dataProcessService.minuteLevelStorage(rawEntity); + }catch (Exception e){ + logger.error("Process data error:{}",e.getMessage()); + } + return JsonResponse.buildSuccess(resultJson); + } + + @RequestMapping(value = "main_receiver", method = RequestMethod.POST) + public JsonResponse mainReceiver(@RequestBody RAWEntity rawEntity) throws ParseException, InterruptedException { + + logger.warn("Company:{} ,Content:{}",new Object[]{rawEntity.getCompany(),rawEntity.getContent()}); + + boolean flag=this.duplicateDataProcessor.removeDuplicateData(rawEntity.getContent()); + + if (!flag){ + return JsonResponse.buildSuccess("success"); + } + + //数据类型筛选 + switch (rawEntity.getCompany()){ + case CompanyConstants.ZIFISENSE: + return zifiReceiver(rawEntity.getContent()); + case CompanyConstants.NBI: + return nbiReceiver(rawEntity.getContent()); + case CompanyConstants.OVIPHONE: + return oviphoneReceiver(rawEntity.getContent()); + case CompanyConstants.NITTAN: + return nittanReceiver(rawEntity.getContent()); + case CompanyConstants.OCR: + return ocrReceiver(rawEntity.getContent()); + case CompanyConstants.DELTA: + return deltaReceiver(rawEntity.getContent()); + case CompanyConstants.METCOM: + return metcomReceiver(rawEntity.getContent()); + } + + + return JsonResponse.buildError(-1,"Invalid data, not from zifisense , NBI , oviphone , ocr , delta"); + } + + //Zifisense设备数据处理入口 + private JsonResponse zifiReceiver(String content){ + if (content.contains("msUid")){ + zetaProcessor.start(content); + return JsonResponse.buildSuccess("Received ZETA Data"); + }else{ + return JsonResponse.buildError(-1,"Received ZETA Data, but not MS upload data"); + } + } + + //nbi设备数据处理接口 + private JsonResponse nbiReceiver(String content){ + JsonResponse jsonResponse = nbiProcessor.start(content); + return jsonResponse; + } + + //oviphone设备数据处理接口 + private JsonResponse oviphoneReceiver(String content) throws ParseException, InterruptedException { + JsonResponse jsonResponse = oviphoneProcessor.start(content); + return jsonResponse; + } + + //nittan设备数据处理接口 + private JsonResponse nittanReceiver(String content){ + JsonResponse jsonResponse = null; + try { + jsonResponse = nittanProcessor.start(content); + } catch (Exception e) { + logger.error("nittan error occurs!! {}",e.getMessage(),e); + jsonResponse = JsonResponse.buildError("Error parsing data."); + } + return jsonResponse; + } + + //ocr设备数据处理接口 + private JsonResponse ocrReceiver(String content){ + JsonResponse jsonResponse = ocrProcessor.start(content); + return jsonResponse; + } + + //delta设备数据处理接口 + private JsonResponse deltaReceiver(String content){ + + JsonResponse jsonResponse = JsonResponse.buildError("deltaReceiver process Error"); + try{ + jsonResponse = deltaIProcessor.start(content); + }catch (Exception e){ + logger.error(e.getMessage()); + } + return jsonResponse; + } + + private JsonResponse metcomReceiver(String content){ + JsonResponse jsonResponse = JsonResponse.buildError("metcomProcessor process Error"); + try{ + jsonResponse = metcomProcessor.start(content); + }catch (Exception e){ + logger.error("metcomReceiver {}",e); + + } + return jsonResponse; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/BaStatusDao.java b/src/main/java/com/techsor/datacenter/sender/dao/BaStatusDao.java new file mode 100644 index 0000000..bf34684 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/BaStatusDao.java @@ -0,0 +1,72 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.bastatus.BaStatusEntity; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Slf4j +@Component +public class BaStatusDao { + + + @Resource + private JdbcTemplate jdbcTemplate; + + /** + * Get all dataList by DeviceId + * @param deviceId + * @return + */ + public BaStatusEntity getByDeviceId(String deviceId) { + String sql="SELECT " + + "ba_status_statistics.id as id,ba_status_statistics.device_info_id,ba_status_statistics.is_running,ba_status_statistics.latest_ts," + + "ba_status_statistics.continuous_running_time,ba_status_statistics.aggregated_running_time,ba_status_statistics.running_count,ba_status_statistics.last_start_time,ba_status_statistics.last_stop_time " + + " FROM ba_status_statistics,device_info " + + "where ba_status_statistics.device_info_id=device_info.id " + + "and device_info.device_id='#deviceId';"; + sql = sql.replaceAll("#deviceId",deviceId); + + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + BaStatusEntity item = new BaStatusEntity(); + item.setId(rs.getInt("id")); + item.setDeviceInfoId(rs.getInt("device_info_id")); + item.setIsRunning(rs.getInt("is_running")); + item.setLatest_ts(rs.getString("latest_ts")); + item.setContinuousRunningTime(rs.getLong("continuous_running_time")); + item.setAggregatedRunningTime(rs.getLong("aggregated_running_time")); + item.setRunningCount(rs.getInt("running_count")); + item.setLastStopTime(rs.getLong("last_stop_time")); + item.setLastStartTime(rs.getLong("last_start_time")); + return item; + }); + + if (dataList.size()==0){ + return null; + }else{ + return dataList.get(dataList.size()-1); + } + } + + public Boolean insert(BaStatusEntity entity) { + String sql= "INSERT INTO `ba_status_statistics` (" + + "`device_info_id`, `is_running`, `latest_ts`, `continuous_running_time`, `aggregated_running_time`, `running_count`, `last_start_time`, `last_stop_time`" + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + int rows = jdbcTemplate.update(sql,entity.getDeviceInfoId(),entity.getIsRunning(),entity.getLatest_ts(),entity.getContinuousRunningTime(),entity.getAggregatedRunningTime(),entity.getRunningCount(),entity.getLastStartTime(),entity.getLastStopTime()); + return (rows>0); + } + + public Boolean update(BaStatusEntity entity) { + String sql= "UPDATE ba_status_statistics SET " + + "device_info_id = ?, is_running = ?, latest_ts = ?, continuous_running_time = ?, aggregated_running_time = ?, running_count = ? , last_start_time = ? , last_stop_time = ? " + + "WHERE id = ?"; + int rows = jdbcTemplate.update(sql,entity.getDeviceInfoId(),entity.getIsRunning(),entity.getLatest_ts(),entity.getContinuousRunningTime(),entity.getAggregatedRunningTime(),entity.getRunningCount(),entity.getLastStartTime(),entity.getLastStopTime(),entity.getId()); + return (rows>0); + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/CommonDAO.java b/src/main/java/com/techsor/datacenter/sender/dao/CommonDAO.java new file mode 100644 index 0000000..4e707b1 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/CommonDAO.java @@ -0,0 +1,124 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.DeviceEntity; + +import cn.hutool.core.collection.CollectionUtil; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.ArrayList; +import java.util.List; + +@Component +public class CommonDAO { + + @Resource + private JdbcTemplate jdbcTemplate ; + private static final Logger logger = LoggerFactory.getLogger(CommonDAO.class); + + + + + //根据设备id获取对应的WS客户端ID + @Cacheable(value = "CommonDAO::getWSClientByDeviceId", key = "#deviceId") + public List getWSClientByDeviceId(String deviceId) { + String sql = ""; + if (StringUtils.isEmpty(deviceId)){ + return new ArrayList<>(); + + } + sql = "SELECT * FROM device_info,jg_ws_clients WHERE device_id='"+deviceId+"' and device_info.wsclient_id = jg_ws_clients.id"; + logger.debug("Execute SQL: {}",sql); + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setId(rs.getInt("id")); + item.setDeviceId(rs.getString("device_id")); + item.setTypeId(rs.getInt("type_id")); + item.setWsClientId(rs.getString("client_id")); + return item; + }); + return dataList; + } + + + //根据设备SN获取对应信息 + @Cacheable(value = "getDeviceInfoBySN", key = "#sn") + public List getDeviceInfoBySN(String sn) { + String sql = ""; + sql = "SELECT * FROM device_info,jg_ws_clients WHERE device_sn='"+sn+"' and device_info.wsclient_id = jg_ws_clients.id"; + logger.debug("Execute SQL: {}",sql); + return getDeviceEntities(sql); + } + + //根据设备DeviceId获取对应信息 + @Cacheable(value = "CommonDAO::getDeviceInfoByDeviceId", key = "#deviceId") + public List getDeviceInfoByDeviceId(String deviceId) { + String sql = ""; + sql = "SELECT * FROM device_info,jg_ws_clients WHERE device_id='"+deviceId+"' and device_info.wsclient_id = jg_ws_clients.id"; + logger.debug("Execute SQL: {}",sql); + return getDeviceEntities(sql); + } + + + public List getDeviceEntities(String sql) { + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setId(rs.getInt("id")); + item.setDeviceId(rs.getString("device_id")); + item.setDeviceSN(rs.getString("device_sn")); + item.setTypeId(rs.getInt("type_id")); + item.setWsClientId(rs.getString("client_id")); + return item; + }); + return dataList; + } + + //根据typeId获取设备信息列表 + public List getDeviceInfoByTypeIds(List typeIdList) { + if (CollectionUtil.isEmpty(typeIdList)){ + return new ArrayList<>(); + } + String sql = ""; + String factorSql = ""; + for (int i=0;i getWsClientIdListByTypeIds(List typeIdList) { + if (CollectionUtil.isEmpty(typeIdList)){ + return new ArrayList<>(); + } + String sql = ""; + String factorSql = ""; + for (int i=0;i dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> rs.getString("client_id")); + return dataList; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/CompanyInfoDao.java b/src/main/java/com/techsor/datacenter/sender/dao/CompanyInfoDao.java new file mode 100644 index 0000000..f8781ec --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/CompanyInfoDao.java @@ -0,0 +1,83 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.company.CompanyEntity; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +@Slf4j +@Component +public class CompanyInfoDao { + + + @Resource + private JdbcTemplate jdbcTemplate; + + @Cacheable(value = "CompanyInfoDao::getCompanyList",key = "'CompanyList'") + public List getCompanyList() { +// String sql="SELECT id,parent_id,company_name,bearer_token,third_api_host FROM data_center_admin.basic_company;"; + String sql="SELECT id,parent_id,company_name,bearer_token,third_api_host FROM data_center_aeon_admin.basic_company;"; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + CompanyEntity item = new CompanyEntity(); + item.setId(rs.getLong("id")); + item.setParentId(rs.getLong("parent_id")); + item.setCompanyName(rs.getString("company_name")); + item.setBearerToken(rs.getString("bearer_token")); + item.setThirdApiHost(rs.getString("third_api_host")); + return item; + }); + + return dataList; + } + + @Cacheable(value = "CompanyInfoDao::getCompanyById",key = "#companyId") + public CompanyEntity getCompanyById(String companyId) { +// String sql="SELECT id,parent_id,company_name,bearer_token,third_api_host FROM data_center_admin.basic_company where id="+companyId; + String sql="SELECT id,parent_id,company_name,bearer_token,third_api_host FROM data_center_aeon_admin.basic_company where id="+companyId; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + CompanyEntity item = new CompanyEntity(); + item.setId(rs.getLong("id")); + item.setParentId(rs.getLong("parent_id")); + item.setCompanyName(rs.getString("company_name")); + item.setBearerToken(rs.getString("bearer_token")); + item.setThirdApiHost(rs.getString("third_api_host")); + return item; + }); + if (dataList.size()==0){ + return null; + }else{ + return dataList.get(0); + } + } + + @Cacheable(value = "CompanyInfoDao::getCompanyList",key = "#companyId") + public long getTopCompanyId(String companyId) { + + String sql="SELECT " + + " bcom.id, bcom.parent_id parentId" + + " FROM data_center_aeon_admin.basic_company bcom " + + " WHERE bcom.flag != 1 and bcom.id = " + companyId; + + AtomicLong parentId = new AtomicLong(0); + AtomicLong id = new AtomicLong(0); + jdbcTemplate.query(sql,rs -> { + parentId.set(rs.getLong("parentId")); + id.set(rs.getLong("id")); + }); + //Recursive logiczA + if (1 == parentId.get() || -1 == parentId.get() || parentId.get() == 0 || parentId.get() == id.get()) { + return id.get(); + } else { + log.error("Notice query,parentId:"+parentId.get()); + return getTopCompanyId(parentId.get()+""); + } + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DashboardAlertDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DashboardAlertDao.java new file mode 100644 index 0000000..ed0f5ab --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/DashboardAlertDao.java @@ -0,0 +1,116 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.AlertHistoryDTO; +import com.techsor.datacenter.sender.entitiy.DynamodbEntity; +import lombok.extern.slf4j.Slf4j; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +@Slf4j +@Component +public class DashboardAlertDao { + + + @Resource + private JdbcTemplate jdbcTemplate; + + public void upsertDeviceRawData(DynamodbEntity entity) { + if (StringUtils.isEmpty(entity.getDeviceId())) { + return; + } + + Integer year = Integer.valueOf(entity.getYearKey()); + Integer month = Integer.valueOf(entity.getMonthKey()); + Integer day = Integer.valueOf(entity.getDayKey()); + + + int updateCount = jdbcTemplate.update( + "UPDATE device_rawdata_realtime SET " + + "building_id = ?, status = ?, receive_ts = ?, alert_title = ?, " + + "alert_content = ?, alert_cancel_title = ?, alert_cancel_content = ?, " + + "raw_data = ?, upload_year = ?, upload_month = ?, upload_day = ? " + + "WHERE device_id = ?", + entity.getDbBuildingId(), entity.getStatus(), entity.getReceive_ts(), entity.getAlertTitle(), + entity.getAlertContent(), entity.getAlertCancelTitle(), entity.getAlertCancelContent(), + entity.getRawData(), year, month, day, + entity.getDeviceId() + ); + + if (updateCount == 0) { + jdbcTemplate.update( + "INSERT INTO device_rawdata_realtime (" + + "device_id, building_id, status, receive_ts, alert_title, alert_content, " + + "alert_cancel_title, alert_cancel_content, raw_data, upload_year, upload_month, upload_day) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + entity.getDeviceId(), entity.getDbBuildingId(), entity.getStatus(), entity.getReceive_ts(), + entity.getAlertTitle(), entity.getAlertContent(), entity.getAlertCancelTitle(), entity.getAlertCancelContent(), + entity.getRawData(), year, month, day + ); + } + } + + public void insertAlertHistory(DynamodbEntity entity) { + String sql = "INSERT INTO alert_history (" + + "device_id, receive_ts, retain_alert" + + ") VALUES (?, ?, ?)"; + + jdbcTemplate.update(sql, + entity.getDeviceId(), + entity.getReceive_ts(), + entity.getRetainAlert() + ); + } + + public void updateLatestAlertToAutoRecovered(DynamodbEntity entity) { + String sql = "SELECT * FROM alert_history WHERE device_id = ? ORDER BY id DESC LIMIT 1"; + + List result = jdbcTemplate.query(sql, new RowMapper() { + @Override + public AlertHistoryDTO mapRow(ResultSet rs, int rowNum) throws SQLException { + AlertHistoryDTO dto = new AlertHistoryDTO(); + dto.setId(rs.getLong("id")); + dto.setDeviceId(rs.getString("device_id")); + dto.setReceiveTs(rs.getLong("receive_ts")); + dto.setConfirmStatus(rs.getInt("confirm_status")); + dto.setHandleStatus(rs.getInt("handle_status")); + dto.setAlertStatus(rs.getInt("alert_status")); + dto.setRetainAlert(rs.getInt("retain_alert")); + return dto; + } + }, entity.getDeviceId()); + + if (CollectionUtils.isEmpty(result)) { + log.debug("Alert history not found"); + return; + } + + AlertHistoryDTO alertHistoryDTO = result.get(0); + + if (1 == alertHistoryDTO.getAlertStatus()) { + log.debug("The alert has been processed"); + return; + } + + Long alertHistoryId = alertHistoryDTO.getId(); + + // 更新 alert_history + String updateAlertHistorySql = "UPDATE alert_history SET handle_status = 4, alert_status = 1 WHERE id = ?"; + jdbcTemplate.update(updateAlertHistorySql, alertHistoryId); + + // 更新 alert_handle_history + // status值不能是完成status != 3 + String updateHandleHistorySql = "UPDATE alert_handle_history SET status = 4, alert_status = 1 WHERE alert_history_id = ? and status != 3"; + jdbcTemplate.update(updateHandleHistorySql, alertHistoryId); + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DashboardStatisticsDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DashboardStatisticsDao.java new file mode 100644 index 0000000..da948f1 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/DashboardStatisticsDao.java @@ -0,0 +1,118 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.StatisticsAccumulateInfo; +import com.techsor.datacenter.sender.entitiy.StatisticsMeasureInfo; + +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +@Slf4j +@Component +public class DashboardStatisticsDao { + + + @Resource + private JdbcTemplate jdbcTemplate; + + public void insertDeviceMeasureInfo(String uploadValue, String deviceId, StatisticsMeasureInfo info) { + String sql = "INSERT INTO dashboard_record_measure " + + "(device_id, date_year, date_month, date_day, date_hour, date_minute, date_second, " + + "upload_value, upload_at) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + jdbcTemplate.update(sql, + deviceId, + info.getYearKey(), + info.getMonthKey(), + info.getDayKey(), + info.getHourKey(), + info.getMinuteKey(), + info.getSecondKey(), + uploadValue, + info.getUploadAt() + ); + } + + public void upsertDeviceRealtimeMeasure(String uploadValue, String deviceId, BigDecimal minValue, + BigDecimal maxValue, StatisticsMeasureInfo info) { + String sql = "INSERT INTO dashboard_realtime_measure (" + + "device_id, date_year, date_month, date_day, date_hour, date_minute, date_second, " + + "upload_value, min_value, max_value, upload_at) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " + + "ON DUPLICATE KEY UPDATE " + + "date_year = VALUES(date_year), " + + "date_month = VALUES(date_month), " + + "date_day = VALUES(date_day), " + + "date_hour = VALUES(date_hour), " + + "date_minute = VALUES(date_minute), " + + "date_second = VALUES(date_second), " + + "upload_value = VALUES(upload_value), " + + "min_value = VALUES(min_value), " + + "max_value = VALUES(max_value), " + + "upload_at = VALUES(upload_at)"; + + jdbcTemplate.update(sql, + deviceId, + info.getYearKey(), + info.getMonthKey(), + info.getDayKey(), + info.getHourKey(), + info.getMinuteKey(), + info.getSecondKey(), + uploadValue, + minValue != null ? minValue.toString() : null, + maxValue != null ? maxValue.toString() : null, + info.getUploadAt() + ); + } + + public void insertDeviceAccumulateInfo(String uploadValue, String deviceId, Double incrementToday, + Double incrementMinute, StatisticsAccumulateInfo info) { + String sql = "INSERT INTO dashboard_record_accumulate " + + "(device_id, date_year, date_month, date_day, date_hour, date_minute, date_second, " + + "upload_value, increment_today, increment_minute, upload_at) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + jdbcTemplate.update(sql, + deviceId, + info.getYearKey(), + info.getMonthKey(), + info.getDayKey(), + info.getHourKey(), + info.getMinuteKey(), + info.getSecondKey(), + uploadValue, + incrementToday, + incrementMinute, + info.getUploadAt() + ); + } + + public void insertOrUpdateRealtimeAccumulateDay(String uploadValue, String deviceId, + Double incrementToday, StatisticsAccumulateInfo info) { + String sql = "INSERT INTO dashboard_realtime_accumulate_day " + + "(device_id, date_year, date_month, date_day, upload_value, increment_today, upload_at) " + + "VALUES (?, ?, ?, ?, ?, ?, ?) " + + "ON DUPLICATE KEY UPDATE " + + "upload_value = VALUES(upload_value), " + + "increment_today = VALUES(increment_today), " + + "upload_at = VALUES(upload_at)"; + + jdbcTemplate.update(sql, + deviceId, + info.getYearKey(), + info.getMonthKey(), + info.getDayKey(), + uploadValue, + incrementToday, + info.getUploadAt() + ); + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DataSrcConfigDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DataSrcConfigDao.java new file mode 100644 index 0000000..b8cdf2a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/DataSrcConfigDao.java @@ -0,0 +1,40 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.DataSrcConfigInfoEntity; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.ArrayList; +import java.util.List; + +@Component +public class DataSrcConfigDao { + + + @Resource + private JdbcTemplate jdbcTemplate ; + + + @Cacheable(value = "DataSrcConfigDao::getDataSrcConfigByCode", key = "#code") + public List getDataSrcConfigByCode(String code){ + + List resultList=new ArrayList<>(); + if (StringUtils.isEmpty(code)){ + return resultList; + } + String sql="select * from data_src_config where code='"+code+"' "; + + resultList=this.jdbcTemplate.query(sql,(rs,rowNum)->{ + DataSrcConfigInfoEntity dataSrcConfigInfoEntity=new DataSrcConfigInfoEntity(); + dataSrcConfigInfoEntity.setCode(rs.getString("code")); + dataSrcConfigInfoEntity.setName(rs.getString("name")); + dataSrcConfigInfoEntity.setMethodType(rs.getString("method_type")); + return dataSrcConfigInfoEntity; + }); + return resultList; + + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DeviceAlertConfigDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DeviceAlertConfigDao.java new file mode 100644 index 0000000..7136f2a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/DeviceAlertConfigDao.java @@ -0,0 +1,63 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.dto.DeviceAlertInfo; +import com.techsor.datacenter.sender.entitiy.DeviceAlertConfigEntity; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + + +@Component +public class DeviceAlertConfigDao { + + + @Resource + private JdbcTemplate jdbcTemplate ; + + + /* + + * id int auto_increment + primary key, + device_config_id bigint not null comment '设备类别配置ID', + alert_name varchar(50) null comment '设备告警名称', + level varchar(20) null comment '告警级别', + contents varchar(200) null comment '告警内容', + type varchar(100) null comment '告警类型', + message varchar(200) null comment '告警信息', + flag int default 0 null comment '0 开启,1关闭' + + **/ + + public List selectCurrentDeviceAlertConfigByDeviceId(Long deviceId){ + String sql="select * from device_alert_config where device_config_id="+deviceId+" and flag!=1"; + + List dataList=this.jdbcTemplate.query(sql,(rs, rowNum)->{ + DeviceAlertConfigEntity deviceAlertConfigEntity=new DeviceAlertConfigEntity(); + deviceAlertConfigEntity.setId(rs.getInt("id")); + deviceAlertConfigEntity.setDeviceConfigId(rs.getLong("device_config_id")); + deviceAlertConfigEntity.setAlertName(rs.getString("alert_name")); + deviceAlertConfigEntity.setLevel(rs.getString("level")); + deviceAlertConfigEntity.setContents(rs.getString("contents")); + deviceAlertConfigEntity.setType(rs.getString("type")); + deviceAlertConfigEntity.setMessage(rs.getString("message")); + deviceAlertConfigEntity.setAlertCancelMessage(rs.getString("alert_cancel_message")); + deviceAlertConfigEntity.setAlertTitle(rs.getString("alert_title")); + deviceAlertConfigEntity.setAlertCancelTitle(rs.getString("alert_cancel_title")); + deviceAlertConfigEntity.setFlag(rs.getInt("flag")); + deviceAlertConfigEntity.setTargetId(rs.getString("target_id")); + deviceAlertConfigEntity.setProblemReportCategoryId(rs.getString("problem_report_category_id")); + deviceAlertConfigEntity.setForwardType(rs.getString("forward_type")); + deviceAlertConfigEntity.setBuildingId(rs.getString("building_id")); + deviceAlertConfigEntity.setBuildingCode(rs.getString("building_code")); + return deviceAlertConfigEntity; + }); + return dataList; + } + + + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DeviceAlertTemplateBindDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DeviceAlertTemplateBindDao.java new file mode 100644 index 0000000..5b8c7d9 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/DeviceAlertTemplateBindDao.java @@ -0,0 +1,20 @@ +package com.techsor.datacenter.sender.dao; + + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Component +public class DeviceAlertTemplateBindDao { + + @Resource + private JdbcTemplate jdbcTemplate; + + public List selectDeviceAlertTemplateBindByDeviceId(Long deviceId){ + String sql="select device_alert_template_id from device_alert_template_bind where device_info_id="+deviceId; + return this.jdbcTemplate.query(sql,(rs, rowNum)-> rs.getLong("device_alert_template_id")); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DeviceAlertTemplateDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DeviceAlertTemplateDao.java new file mode 100644 index 0000000..672293b --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/DeviceAlertTemplateDao.java @@ -0,0 +1,89 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.DeviceAlertTemplateEntity; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +import com.techsor.datacenter.sender.dto.DeviceAlertInfo; +import java.util.List; + +/* +* /*** + * ( + * id bigint auto_increment + * primary key, + * company_id bigint null, + * tmpl_name varchar(100) null, + * level varchar(20) null comment '告警级别', + * message varchar(999) null comment '告警信息', + * flag int default 0 null comment '0 开启,1关闭', + * template_defs varchar(999) null comment '模板定义', + * forward_type varchar(20) null comment '转发类型api, mail, sms', + * alert_cancel_template_defs varchar(999) default '' null comment '告警解除模板定义', + * alert_cancel_message varchar(999) default '' null comment '告警解除模板初步填充后内容', + * alert_title varchar(999) null, + * alert_cancel_title varchar(999) null, + * alert_recipient_mail varchar(1000) null comment '邮件通知对象', + * alert_recipient_sms varchar(1000) null comment '短信通知对象' + * ) +* */ +@Component +public class DeviceAlertTemplateDao { + + @Resource + private JdbcTemplate jdbcTemplate; + + public List selectDeviceAlertTemplatesByDeviceId(Long deviceId) { + String sql = "SELECT tmp.id, " + + "tmp.company_id AS companyId, " + + "tmp.tmpl_name AS tmplName, " + + "tmp.level As level, " + + "tmp.message As message, " + + "tmp.flag, " + + "tmp.template_defs AS templateDefs, " + + "tmp.forward_type AS forwardType, " + + "tmp.alert_cancel_template_defs AS alertCancelTemplateDefs, " + + "tmp.alert_cancel_message AS alertCancelMessage, " + + "tmp.alert_title AS alertTitle, " + + "tmp.alert_cancel_title AS alertCancelTitle, " + + "tmp.alert_recipient_mail AS alertRecipientMail, " + + "datb.device_info_id AS deviceConfigId, " + + "dac.contents As contents, " + + "dac.type As deviceTypeName, " + + "dac.target_id AS targetId, " + + "dac.problem_report_category_id AS problemReportCategoryId, " + + "dac.building_id AS buildingId, " + + "dac.building_code AS buildingCode " + + "FROM device_alert_template tmp " + + "LEFT JOIN device_alert_template_bind datb ON tmp.id = datb.device_alert_template_id " + + "LEFT JOIN device_alert_config dac ON dac.device_config_id = datb.device_info_id " + + "WHERE tmp.flag = 0 AND dac.flag = 0 AND datb.device_info_id = "+deviceId; + + return this.jdbcTemplate.query(sql, (rs, rowNum) -> { + DeviceAlertInfo deviceAlertTemplateEntity = new DeviceAlertInfo(); + deviceAlertTemplateEntity.setId(rs.getInt("id")); + deviceAlertTemplateEntity.setCompanyId(rs.getLong("companyId")); + deviceAlertTemplateEntity.setTmplName(rs.getString("tmplName")); + deviceAlertTemplateEntity.setLevel(rs.getString("level")); + deviceAlertTemplateEntity.setMessage(rs.getString("message")); + deviceAlertTemplateEntity.setFlag(rs.getInt("flag")); + deviceAlertTemplateEntity.setTemplateDefs(rs.getString("templateDefs")); + deviceAlertTemplateEntity.setForwardType(rs.getString("forwardType")); + deviceAlertTemplateEntity.setAlertCancelTemplateDefs(rs.getString("alertCancelTemplateDefs")); + deviceAlertTemplateEntity.setAlertCancelMessage(rs.getString("alertCancelMessage")); + deviceAlertTemplateEntity.setAlertTitle(rs.getString("alertTitle")); + deviceAlertTemplateEntity.setAlertCancelTitle(rs.getString("alertCancelTitle")); + deviceAlertTemplateEntity.setAlertRecipientMail(rs.getString("alertRecipientMail")); + deviceAlertTemplateEntity.setDeviceConfigId(rs.getLong("deviceConfigId")); + deviceAlertTemplateEntity.setContents(rs.getString("contents")); + deviceAlertTemplateEntity.setType(rs.getString("deviceTypeName")); + deviceAlertTemplateEntity.setTargetId(rs.getString("targetId")); + deviceAlertTemplateEntity.setProblemReportCategoryId(rs.getString("problemReportCategoryId")); + deviceAlertTemplateEntity.setBuildingId(rs.getString("buildingId")); + deviceAlertTemplateEntity.setBuildingCode(rs.getString("buildingCode")); + return deviceAlertTemplateEntity; + }); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DeviceConfigInfoDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DeviceConfigInfoDao.java new file mode 100644 index 0000000..e533165 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/DeviceConfigInfoDao.java @@ -0,0 +1,172 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.DeviceConfigInfoEntity; +import com.techsor.datacenter.sender.entitiy.metcom.MetcomSpaceBleEntity; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Slf4j +@Component +public class DeviceConfigInfoDao { + + + @Resource + private JdbcTemplate jdbcTemplate; + + + /** + * create table type + * ( + * id int auto_increment + * primary key, + * name varchar(45) null comment '设备ID', + * description varchar(999) null comment '备注', + * device_type varchar(200) null comment '设备类别', + * device_data_src varchar(200) null comment '设备数据来源', + * origin_json_format varchar(2000) null comment '原始数据来源', + * expression_map varchar(200) null comment '表达式映射', + * expression_variable_map varchar(500) null comment '表达式变量map', + * target_json_format varchar(2000) null comment '转发数据格式', + * target_forward_id bigint null comment '转发目标ID', + * target_foward_code varchar(100) null comment '转发识别代码', + * created_by bigint null comment '创建人', + * created_timestamp timestamp null comment '创建时间', + * updated_by bigint null, + * updated_timestamp timestamp null comment '创建时间', + * company_id bigint null + * ) + * comment '设备类别及备注,帮助识别设备类型和解析方式'; + * + * **/ + /** + * 根据设备ID获取设备配置信息 + * @param deviceId 对应到表中type的name字段 + * + * **/ + @Cacheable(value = "DeviceConfigInfoDao::getDeviceConfigInfo",key = "#deviceId") + public DeviceConfigInfoEntity getDeviceConfigInfo(String deviceId,Long deviceTypeId) { + + String sql="select * from `type` where name='%s' LIMIT 1"; + sql=String.format(sql,deviceId); + log.info("Execute SQL: {}",sql); + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceConfigInfoEntity item = new DeviceConfigInfoEntity(); + item.setId(rs.getInt("id")); + item.setName(rs.getString("name")); + item.setDescription(rs.getString("description")); + item.setDeviceType(rs.getString("device_type")); + item.setDeviceDataSrc(rs.getString("device_data_src")); + item.setOriginJsonFormat(rs.getString("origin_json_format")); + item.setExpressionMap(rs.getString("expression_map")); + item.setExpressionVariableMap(rs.getString("expression_variable_map")); + item.setTargetJsonFormat(rs.getString("target_json_format")); + item.setTargetForwardId(rs.getLong("target_forward_id")); + item.setTargetFowardCode(rs.getString("target_foward_code")); + return item; + }); + + if (dataList.size()>0){ + return dataList.get(0); + }else { + return null; + } + } + + @Cacheable(value = "DeviceConfigInfoDao::getDeviceConfigInfoById",key = "#typeId") + public DeviceConfigInfoEntity getDeviceConfigInfoById(Long typeId) { + + String sql="select * from `type` where id="+typeId+" LIMIT 1"; + log.info("Execute SQL: {}",sql); + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceConfigInfoEntity item = new DeviceConfigInfoEntity(); + item.setId(rs.getInt("id")); + item.setName(rs.getString("name")); + item.setDescription(rs.getString("description")); + item.setDeviceType(rs.getString("device_type")); + item.setDeviceDataSrc(rs.getString("device_data_src")); + item.setOriginJsonFormat(rs.getString("origin_json_format")); + item.setExpressionMap(rs.getString("expression_map")); + item.setExpressionVariableMap(rs.getString("expression_variable_map")); + item.setTargetJsonFormat(rs.getString("target_json_format")); + item.setTargetForwardId(rs.getLong("target_forward_id")); + item.setTargetFowardCode(rs.getString("target_foward_code")); + return item; + }); + + if (dataList.size()>0){ + return dataList.get(0); + }else { + return null; + } + } + + + + @Cacheable(value = "DeviceConfigInfoDao::getDeviceConfigInfo",key = "#code") + public DeviceConfigInfoEntity getDeviceConfigInfo(String code) { + + String sql="select * from `type` where device_data_src='%s' LIMIT 1"; + sql=String.format(sql,code); + log.info("Execute SQL: {}",sql); + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceConfigInfoEntity item = new DeviceConfigInfoEntity(); + item.setId(rs.getInt("id")); + item.setName(rs.getString("name")); + item.setDescription(rs.getString("description")); + item.setDeviceType(rs.getString("device_type")); + item.setDeviceDataSrc(rs.getString("device_data_src")); + item.setOriginJsonFormat(rs.getString("origin_json_format")); + item.setExpressionMap(rs.getString("expression_map")); + item.setExpressionVariableMap(rs.getString("expression_variable_map")); + item.setTargetJsonFormat(rs.getString("target_json_format")); + item.setTargetForwardId(rs.getLong("target_forward_id")); + item.setTargetFowardCode(rs.getString("target_foward_code")); + return item; + }); + + if (dataList.size()>0){ + return dataList.get(0); + }else { + return null; + } + } + + + @Cacheable(value = "DeviceConfigInfoDao::getDeviceTypeConfigInfoById",key = "#typeId") + public DeviceConfigInfoEntity getDeviceTypeConfigInfoById(Long typeId) { + + String sql="select * from `type` where id=%d LIMIT 1"; + sql=String.format(sql,typeId); + log.info("Execute SQL: {}",sql); + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceConfigInfoEntity item = new DeviceConfigInfoEntity(); + item.setId(rs.getInt("id")); + item.setName(rs.getString("name")); + item.setDescription(rs.getString("description")); + item.setDeviceType(rs.getString("device_type")); + item.setDeviceDataSrc(rs.getString("device_data_src")); + item.setOriginJsonFormat(rs.getString("origin_json_format")); + item.setExpressionMap(rs.getString("expression_map")); + item.setExpressionVariableMap(rs.getString("expression_variable_map")); + item.setTargetJsonFormat(rs.getString("target_json_format")); + item.setTargetForwardId(rs.getLong("target_forward_id")); + item.setTargetFowardCode(rs.getString("target_foward_code")); + return item; + }); + + if (dataList.size()>0){ + return dataList.get(0); + }else { + return null; + } + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DeviceDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DeviceDao.java new file mode 100644 index 0000000..8dd3381 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/DeviceDao.java @@ -0,0 +1,279 @@ +package com.techsor.datacenter.sender.dao; + +import com.alibaba.fastjson2.JSON; +import com.techsor.datacenter.sender.dto.DeviceInfoVO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.UdfAssetInfo; +import com.techsor.datacenter.sender.entitiy.UdfCommonInfo; +import com.techsor.datacenter.sender.entitiy.UdfCommonJsonInfo; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Component +public class DeviceDao { + + @Resource + private JdbcTemplate jdbcTemplate ; + + @Cacheable(value = "DeviceDao::getUdfCommonDtoByName", key = "#id+#tableName") + public List getUdfCommonDtoByName(Long id,String tableName){ + List resultList=new ArrayList<>(); + if (Objects.isNull(id) || StringUtils.isEmpty(tableName)){ + return resultList; + } + + switch (tableName){ + case "basic_project":{ + String sql="select * from "+tableName+" where id="+id+" "; + resultList=this.jdbcTemplate.query(sql,(rs,rowNum)->{ + UdfCommonInfo udfCommonInfo=new UdfCommonInfo(); + udfCommonInfo.setId(rs.getString("udf_project_id")); + udfCommonInfo.setName(rs.getString("project_name")); + UdfCommonJsonInfo commonJsonInfo = new UdfCommonJsonInfo(); + commonJsonInfo.setData(JSON.toJSONString(udfCommonInfo)); + return commonJsonInfo; + }); + break; + } + case "basic_building":{ + String sql="select * from "+tableName+" where building_id ="+id+" "; + resultList=this.jdbcTemplate.query(sql,(rs,rowNum)->{ + UdfCommonInfo udfCommonInfo=new UdfCommonInfo(); + udfCommonInfo.setId(rs.getString("udf_building_id")); + udfCommonInfo.setName(rs.getString("name")); + UdfCommonJsonInfo commonJsonInfo = new UdfCommonJsonInfo(); + commonJsonInfo.setData(JSON.toJSONString(udfCommonInfo)); + return commonJsonInfo; + }); + break; + } + case "basic_space":{ + String sql="select * from "+tableName+" where space_id ="+id+" "; + resultList=this.jdbcTemplate.query(sql,(rs,rowNum)->{ + UdfCommonInfo udfCommonInfo=new UdfCommonInfo(); + udfCommonInfo.setId(rs.getString("udf_space_id")); + udfCommonInfo.setName(rs.getString("name")); + UdfCommonJsonInfo commonJsonInfo = new UdfCommonJsonInfo(); + commonJsonInfo.setData(JSON.toJSONString(udfCommonInfo)); + return commonJsonInfo; + }); + break; + } + case "basic_floor":{ + String sql="select * from "+tableName+" where floor_id ="+id+" "; + resultList=this.jdbcTemplate.query(sql,(rs,rowNum)->{ + UdfCommonInfo udfCommonInfo=new UdfCommonInfo(); + udfCommonInfo.setId(rs.getString("udf_floor_id")); + udfCommonInfo.setName(rs.getString("name")); + UdfCommonJsonInfo commonJsonInfo = new UdfCommonJsonInfo(); + commonJsonInfo.setData(JSON.toJSONString(udfCommonInfo)); + return commonJsonInfo; + }); + break; + } + case "basic_monitoring_asset":{ + try{ + String sql="select basic_monitoring_asset.*,basic_asset_class_big.class_name as classBigName,basic_asset_class_medium.class_name as classMediumName,basic_asset_class_small.class_name as classSmallName from " + + "basic_monitoring_asset,basic_asset_class_big,basic_asset_class_medium,basic_asset_class_small where basic_monitoring_asset.equipment_id ="+id+" and basic_monitoring_asset.class_big_id = basic_asset_class_big.id" + + " and basic_monitoring_asset.class_medium_id = basic_asset_class_medium.id " + + " and basic_monitoring_asset.class_small_id = basic_asset_class_small.id "; + resultList=this.jdbcTemplate.query(sql,(rs,rowNum)->{ + UdfCommonJsonInfo commonJsonInfo = new UdfCommonJsonInfo(); + UdfAssetInfo udfAssetInfo=new UdfAssetInfo(); + udfAssetInfo.setId(rs.getString("udf_equipment_id")); + udfAssetInfo.setAssetName(rs.getString("name")); + udfAssetInfo.setAssetClassBig(rs.getString("classBigName")); + udfAssetInfo.setAssetClassMedium(rs.getString("classMediumName")); + udfAssetInfo.setAssetClassSmall(rs.getString("classSmallName")); + udfAssetInfo.setAssetSymbol(rs.getString("symbol")); + udfAssetInfo.setAssetManufacturer(rs.getString("manufacturer")); + udfAssetInfo.setAssetModel(rs.getString("model")); + udfAssetInfo.setAssetManufacturingDate(rs.getString("manufacturing_date")); + udfAssetInfo.setAssetSpec1(rs.getString("specifications1")); + udfAssetInfo.setAssetSpec2(rs.getString("specifications2")); + udfAssetInfo.setAssetSpec3(rs.getString("specifications3")); + udfAssetInfo.setAssetSpec4(rs.getString("specifications4")); + udfAssetInfo.setAssetSpec5(rs.getString("specifications5")); + udfAssetInfo.setAssetSpec6(rs.getString("specifications6")); + commonJsonInfo.setData(JSON.toJSONString(udfAssetInfo)); + return commonJsonInfo; + }); + }catch (Exception e){ + //Temporary adaptation for updating without losing data + String sql="select * from basic_monitoring_asset where basic_monitoring_asset.equipment_id ="+id; + resultList=this.jdbcTemplate.query(sql,(rs,rowNum)->{ + UdfCommonJsonInfo commonJsonInfo = new UdfCommonJsonInfo(); + UdfAssetInfo udfAssetInfo=new UdfAssetInfo(); + udfAssetInfo.setId(rs.getString("udf_equipment_id")); + udfAssetInfo.setAssetName(rs.getString("name")); + udfAssetInfo.setAssetClassBig(rs.getString("class_big")); + udfAssetInfo.setAssetClassMedium(rs.getString("class_medium")); + udfAssetInfo.setAssetClassSmall(rs.getString("class_small")); + udfAssetInfo.setAssetSymbol(rs.getString("symbol")); + udfAssetInfo.setAssetManufacturer(rs.getString("manufacturer")); + udfAssetInfo.setAssetModel(rs.getString("model")); + udfAssetInfo.setAssetManufacturingDate(rs.getString("manufacturing_date")); + udfAssetInfo.setAssetSpec1(rs.getString("specifications1")); + udfAssetInfo.setAssetSpec2(rs.getString("specifications2")); + udfAssetInfo.setAssetSpec3(rs.getString("specifications3")); + udfAssetInfo.setAssetSpec4(rs.getString("specifications4")); + udfAssetInfo.setAssetSpec5(rs.getString("specifications5")); + udfAssetInfo.setAssetSpec6(rs.getString("specifications6")); + commonJsonInfo.setData(JSON.toJSONString(udfAssetInfo)); + return commonJsonInfo; + }); + } + break; + } + + } + + return resultList; + + } + + //根据设备id获取对应的WS客户端ID + @Cacheable(value = "DeviceDao::getDeviceInfoByDeviceId", key = "#deviceId") + public List getDeviceInfoByDeviceId(String deviceId){ + if (StringUtils.isEmpty(deviceId)){ + return new ArrayList<>(); + } + String sql = ""; + sql = "SELECT id,retain_alert,company_id,device_id,device_sn,type_id,space_id,building_id,device_name,project_id,floor_id,asset_id FROM device_info WHERE device_id='"+deviceId+"' and flag!=1"; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setId(rs.getInt("id")); + item.setCompanyId(rs.getLong("company_id")); + item.setDeviceId(rs.getString("device_id")); + item.setDeviceSN(rs.getString("device_sn")); + item.setTypeId(rs.getInt("type_id")); + item.setSpaceId(rs.getLong("space_id")); + item.setBuildingId(rs.getLong("building_id")); + item.setDeviceName(rs.getString("device_name")); + item.setProjectId(rs.getLong("project_id")); + item.setFloorId(rs.getLong("floor_id")); + item.setAssetId(rs.getLong("asset_id")); + item.setRetainAlert(rs.getInt("retain_alert")); + return item; + }); + return dataList; + } + + @Cacheable(value = "DeviceDao::getDeviceIdBySN", key = "#deviceSN") + public List getDeviceIdBySN(String deviceSN) { + String sql = ""; + sql = "SELECT * FROM device_info WHERE device_sn='"+deviceSN+"' and flag!=1"; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setId(rs.getInt("id")); + item.setDeviceId(rs.getString("device_id")); + item.setTypeId(rs.getInt("type_id")); + item.setWsClientId(rs.getString("wsclient_id")); + return item; + }); + return dataList; + } + + @Cacheable(value = "DeviceDao::getDeviceIdById", key = "#deviceId") + public List getByDeviceId(String deviceId) { + String sql = ""; + sql = "SELECT * FROM device_info WHERE device_id='"+deviceId+"' and flag!=1"; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setId(rs.getInt("id")); + item.setCompanyId(rs.getLong("company_id")); + item.setDeviceId(rs.getString("device_id")); + item.setTypeId(rs.getInt("type_id")); + item.setWsClientId(rs.getString("wsclient_id")); + return item; + }); + return dataList; + } + + // Get the corresponding WS client ID based on the device ID + @Cacheable(value = "DeviceDao::getDeviceInfoByDeviceIdWithDB", key = "#deviceId + '::' + #companyId") + public List getDeviceInfoByDeviceIdWithDB(String deviceId,String companyId){ + if (StringUtils.isEmpty(deviceId)){ + return new ArrayList<>(); + } + String sql = ""; + sql = "SELECT id,company_id,device_id,device_sn,type_id,space_id,building_id,device_name,project_id,floor_id,asset_id FROM data_center_aeon_"+companyId+".device_info WHERE device_id='"+deviceId+"' and flag!=1"; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setId(rs.getInt("id")); + item.setCompanyId(rs.getLong("company_id")); + item.setDeviceId(rs.getString("device_id")); + item.setDeviceSN(rs.getString("device_sn")); + item.setTypeId(rs.getInt("type_id")); + item.setSpaceId(rs.getLong("space_id")); + item.setBuildingId(rs.getLong("building_id")); + item.setDeviceName(rs.getString("device_name")); + item.setProjectId(rs.getLong("project_id")); + item.setFloorId(rs.getLong("floor_id")); + item.setAssetId(rs.getLong("asset_id")); + return item; + }); + return dataList; + } + + + public List getDeviceInfo(String deviceId){ + if (StringUtils.isEmpty(deviceId)){ + return new ArrayList<>(); + } + String sql = ""; + sql = "SELECT\r\n" + + " dinfo.device_id deviceId,\r\n" + + " dinfo.device_sn deviceSn,\r\n" + + " dinfo.device_name deviceName,\r\n" + + " dinfo.monitoring_point_name monitoringPointName,\r\n" + + " ty.unit typeUnit,\r\n" + + " basset.space_id spaceId,\r\n" + + " bspace.name spaceName,\r\n" + + " bfloor.floor_id floorId,\r\n" + + " bfloor.name floorName,\r\n" + + " bbuilding.building_id buildingId,\r\n" + + " bbuilding.name buildingName,\r\n" + + " basset.equipment_id assetId,\r\n" + + " basset.name assetName,\r\n" + + " dct.category_name_en category\r\n" + + "FROM\r\n" + + " device_info dinfo\r\n" + + "INNER JOIN basic_monitoring_asset basset ON dinfo.asset_id = basset.equipment_id\r\n" + + "INNER JOIN basic_space bspace ON bspace.space_id = basset.space_id\r\n" + + "INNER JOIN basic_floor bfloor ON bfloor.floor_id = bspace.floor_id\r\n" + + "INNER JOIN basic_building bbuilding ON bfloor.building_id = bbuilding.building_id\r\n" + + "LEFT JOIN type ty ON ty.id = dinfo.type_id\r\n" + + "LEFT JOIN device_category dct ON dct.id = ty.device_category_id\r\n" + + "WHERE dinfo.flag != 1 AND basset.flag != 1 AND bspace.flag != 1 AND bfloor.flag != 1 AND bbuilding.flag != 1 AND\r\n" + + "dinfo.device_id='"+deviceId+"'"; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceInfoVO item = new DeviceInfoVO(); + item.setDeviceId(rs.getString("deviceId")); + item.setDeviceSn(rs.getString("deviceSn")); + item.setDeviceName(rs.getString("deviceName")); + item.setTypeUnit(rs.getString("typeUnit")); + item.setSpaceName(rs.getString("spaceName")); + item.setFloorName(rs.getString("floorName")); + item.setBuildingName(rs.getString("buildingName")); + item.setAssetName(rs.getString("assetName")); + item.setCategory(rs.getString("category")); + item.setMonitoringPointName(rs.getString("monitoringPointName")); + return item; + }); + return dataList; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DeviceForwardConfigDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DeviceForwardConfigDao.java new file mode 100644 index 0000000..6dc8446 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/DeviceForwardConfigDao.java @@ -0,0 +1,29 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.DeviceForwardConfigEntity; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Component +public class DeviceForwardConfigDao { + + @Resource + private JdbcTemplate jdbcTemplate ; + + + public List selectDeviceForwardConfigByDeviceId(Long deviceId){ + String sql="select * from device_forward_config where device_id="+deviceId; + sql=String.format(sql,deviceId); + List dataList=this.jdbcTemplate.query(sql,(rs, rowNum)->{ + DeviceForwardConfigEntity deviceForwardConfigEntity=new DeviceForwardConfigEntity(); + deviceForwardConfigEntity.setId(Long.valueOf(rs.getInt("id"))); + deviceForwardConfigEntity.setDeviceId(rs.getLong("device_id")); + deviceForwardConfigEntity.setTargetForwardId(rs.getLong("target_forward_id")); + return deviceForwardConfigEntity; + }); + return dataList; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/KingIOServerDAO.java b/src/main/java/com/techsor/datacenter/sender/dao/KingIOServerDAO.java new file mode 100644 index 0000000..c80ae4a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/KingIOServerDAO.java @@ -0,0 +1,37 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Component +public class KingIOServerDAO { + + @Resource + private JdbcTemplate jdbcTemplate ; + + //Get Wsclient info by deviceId + @Cacheable(value = "KingIOServerDAO::queryDeviceWsClientIdByDeviceId", key = "#deviceId") + public DeviceEntity queryDeviceWsClientIdByDeviceId(String deviceId) { + String sql = "SELECT device_id,device_sn,wsclient_id,type_id FROM device_info WHERE device_id='"+deviceId+"' and flag!=1"; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setDeviceId(rs.getString("device_id")); + item.setDeviceSN(rs.getString("device_sn")); + item.setWsClientId(rs.getString("wsclient_id")); + item.setTypeId(rs.getInt("type_id")); + return item; + }); + if (dataList.size()==0){ + return null; + }else{ + return dataList.get(dataList.size()-1); + } + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/MetcomDAO.java b/src/main/java/com/techsor/datacenter/sender/dao/MetcomDAO.java new file mode 100644 index 0000000..7bfa4cd --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/MetcomDAO.java @@ -0,0 +1,100 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.metcom.MetcomEntity; +import com.techsor.datacenter.sender.entitiy.metcom.MetcomRecordEntity; +import com.techsor.datacenter.sender.entitiy.metcom.MetcomSpaceBleEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Component +public class MetcomDAO { + @Resource + private JdbcTemplate jdbcTemplate; + private static final Logger logger = LoggerFactory.getLogger(MetcomDAO.class); + + /** + * 根据SpaceId获取BleId + * @param spaceId + * @return + */ + public MetcomSpaceBleEntity getBleBySpace(String spaceId) { + String sql = "SELECT * FROM metcom_ble_space_info WHERE space_id='?1' order by id desc LIMIT 1 "; + sql = sql.replace("?1",spaceId); + + logger.debug("Execute SQL: {}",sql); + + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + MetcomSpaceBleEntity item = new MetcomSpaceBleEntity(); + item.setSpaceId(rs.getString("space_id")); + item.setBleId(rs.getString("ble_id")); + return item; + }); + + if (dataList.size()>0){ + return dataList.get(0); + }else { + return null; + } + + } + + /** + * 添加进入记录 + * @param uuid + * @param spaceId + * @param bleId + * @return + */ + public Boolean addRecord(String uuid,String spaceId,String bleId) { + String sql = "INSERT INTO metcom_locate_record(uuid,space_id,ble_id) VALUES ('?1','?2','?3') on duplicate key update space_id='?4',ble_id='?5';"; + sql = sql.replace("?1", uuid); + sql = sql.replace("?2", spaceId); + sql = sql.replace("?3",bleId); + sql = sql.replace("?4", spaceId); + sql = sql.replace("?5",bleId); + + logger.debug("Execute SQL: {}",sql); + Integer rows = this.jdbcTemplate.update(sql); + if (rows>0){ + return true; + }else{ + return false; + } + } + + /** + * 根据uuid获取历史记录 + * @param metcomEntity + */ + @Cacheable(value = "MetcomDAO::getRecordByUuid",key = "#metcomEntity.hashCode()") + public MetcomRecordEntity getRecordByUuid(MetcomEntity metcomEntity) { + String sql = "SELECT * FROM metcom_locate_record WHERE uuid='?1' order by id desc LIMIT 1 "; + sql = sql.replace("?1", metcomEntity.getUuid()); + + logger.debug("Execute SQL: {}",sql); + + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + MetcomRecordEntity item = new MetcomRecordEntity(); + item.setSpaceId(rs.getString("space_id")); + item.setBleId(rs.getString("ble_id")); + item.setUuid(rs.getString("uuid")); + return item; + }); + + if (dataList.size()>0){ + return dataList.get(0); + }else { + return null; + } + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/MqttConfigDao.java b/src/main/java/com/techsor/datacenter/sender/dao/MqttConfigDao.java new file mode 100644 index 0000000..3716cd4 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/MqttConfigDao.java @@ -0,0 +1,58 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.MqttConfigEntity; +import com.techsor.datacenter.sender.entitiy.bastatus.BaStatusEntity; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Slf4j +@Component +public class MqttConfigDao { + + + @Resource + private JdbcTemplate jdbcTemplate; + + /** + * Get all dataList by DeviceId + * @param deviceId + * @param purposeType + * @return + */ + public List getByDeviceId(String deviceId, int purposeType) { + String sql="SELECT " + + "bmqtt.id mqttId, bmqtt.company_id, bcomp.company_name, " + + "bmqtt.mqtt_name, bmqtt.mqtt_url, bmqtt.mqtt_username, " + + "bmqtt.mqtt_password, bmqtt.mqtt_topic, bmqtt.apikey " + + "FROM mqtt_config bmqtt " + + "INNER JOIN data_center_aeon_admin.basic_company bcomp ON bcomp.id = bmqtt.company_id " + + "INNER JOIN mqtt_device_relation mdr ON mdr.mqtt_id = bmqtt.id " + + "INNER JOIN device_info dinfo ON dinfo.id = mdr.device_id " + + "WHERE bmqtt.flag != 1 AND bcomp.flag != 1 AND dinfo.flag != 1 AND dinfo.device_id = '#deviceId' AND bmqtt.purpose_type = '#purposeType';"; + sql = sql.replaceAll("#deviceId",deviceId); + sql = sql.replaceAll("#purposeType",purposeType+""); + + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + MqttConfigEntity item = new MqttConfigEntity(); + item.setApikey(rs.getString("apikey")); + item.setCompanyId(rs.getLong("company_id")); + item.setCompanyName(rs.getString("company_name")); + item.setMqttId(rs.getLong("mqttId")); + item.setMqttName(rs.getString("mqtt_name")); + item.setMqttPassword(rs.getString("mqtt_password")); + item.setMqttTopic(rs.getString("mqtt_topic")); + item.setMqttUrl(rs.getString("mqtt_url")); + item.setMqttUsername(rs.getString("mqtt_username")); + return item; + }); + + return dataList; + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/NbiDAO.java b/src/main/java/com/techsor/datacenter/sender/dao/NbiDAO.java new file mode 100644 index 0000000..8af4c59 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/NbiDAO.java @@ -0,0 +1,35 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Component +public class NbiDAO { + + @Resource + private JdbcTemplate jdbcTemplate ; + + //根据设备SN获取对应的DeviceId,用户对接 + @Cacheable(value = "NbiDAO::getDeviceIdBySN", key = "#deviceSN") + public List getDeviceIdBySN(String deviceSN) { + String sql = ""; + sql = "SELECT * FROM device_info WHERE device_sn='"+deviceSN+"'"; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setId(rs.getInt("id")); + item.setDeviceId(rs.getString("device_id")); + item.setTypeId(rs.getInt("type_id")); + item.setWsClientId(rs.getString("wsclient_id")); + return item; + }); + return dataList; + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/NittanCompanyDAO.java b/src/main/java/com/techsor/datacenter/sender/dao/NittanCompanyDAO.java new file mode 100644 index 0000000..0818b64 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/NittanCompanyDAO.java @@ -0,0 +1,56 @@ +package com.techsor.datacenter.sender.dao; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +@Component +public class NittanCompanyDAO { + @Resource + private JdbcTemplate jdbcTemplate ; + + public List> getCompanyList() { + String sql = "select * from company"; + List> list = jdbcTemplate.queryForList(sql); + for (Map map : list) { + Set> entries = map.entrySet( ); + if(entries != null) { + Iterator> iterator = entries.iterator( ); + while(iterator.hasNext( )) { + Map.Entry entry =(Map.Entry) iterator.next( ); + Object key = entry.getKey( ); + Object value = entry.getValue(); + System.out.println(key+":"+value); + } + } + } + return list; + } + @Cacheable(value = "NittanCompanyDAO::getCompanyByCompanyCode",key = "#companyCode") + public List> getCompanyByCompanyCode(String companyCode) { + String sql = "select * from company where company_code=\""+companyCode+"\""; + List> list = jdbcTemplate.queryForList(sql); + for (Map map : list) { + Set> entries = map.entrySet( ); + if(entries != null) { + Iterator> iterator = entries.iterator( ); + while(iterator.hasNext( )) { + Map.Entry entry =(Map.Entry) iterator.next( ); + Object key = entry.getKey( ); + Object value = entry.getValue(); + System.out.println(key+":"+value); + } + } + } + return list; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/NittanDAO.java b/src/main/java/com/techsor/datacenter/sender/dao/NittanDAO.java new file mode 100644 index 0000000..7870df2 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/NittanDAO.java @@ -0,0 +1,42 @@ +package com.techsor.datacenter.sender.dao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +@Component +public class NittanDAO { + @Resource + private JdbcTemplate jdbcTemplate ; + private static final Logger logger = LoggerFactory.getLogger(NittanDAO.class); + + @Cacheable(value = "NittanDAO::getDBMCode",key = "#deviceClass+#noticeType+#noticeLevel+#noticeStatus+#facilityMode") + public List> getDBMCode(String deviceClass,String noticeType,String noticeLevel,String noticeStatus,String facilityMode) { + String sql = "select * from nittan_code_match where device_class='?1' AND notice_type='?2' AND notice_level='?3' AND notice_status='?4' AND facility_mode='?5'"; + sql = sql.replace("?1",deviceClass); + sql = sql.replace("?2",noticeType); + sql = sql.replace("?3",noticeLevel+""); + sql = sql.replace("?4",noticeStatus); + sql = sql.replace("?5",facilityMode); + + logger.debug("Execute SQL: {}",sql); + + return jdbcTemplate.queryForList(sql); + } + + + public List> getJGWSClient() { + String sql = "select * from jg_ws_clients "; + logger.debug("Execute SQL: {}",sql); + + return jdbcTemplate.queryForList(sql); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/OcrDao.java b/src/main/java/com/techsor/datacenter/sender/dao/OcrDao.java new file mode 100644 index 0000000..c41c48b --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/OcrDao.java @@ -0,0 +1,45 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.OcrHistoryEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; +@Component +public class OcrDao { + @Resource + private JdbcTemplate jdbcTemplate ; + private static final Logger logger = LoggerFactory.getLogger(CommonDAO.class); + //根据设备SN获取对应信息 + @Cacheable(value = "OcrDao::getOcrInfoBySN", key = "#sn") + public List getOcrInfoBySN(String sn) { + String sql = "SELECT * FROM ocr_history WHERE device_sn="+sn+" ORDER BY id DESC limit 1"; + logger.debug("Execute SQL: {}",sql); + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + OcrHistoryEntity item = new OcrHistoryEntity(); + item.setId(rs.getInt("id")); + item.setDeviceSN(rs.getString("device_sn")); + item.setValue(rs.getDouble("value")); + item.setTs(rs.getString("ts")); + return item; + }); + return dataList; + } + // 保存ocr数据 + public boolean saveOcrInfo(OcrHistoryEntity data) { + try { + String sql = "INSERT INTO ocr_history(device_sn,value,ts) VALUES ("+ data.getDeviceSN()+","+ data.getValue()+","+ data.getTs()+")"; + logger.debug("Execute SQL: {}",sql); + jdbcTemplate.update(sql); + }catch (Exception err){ + logger.error("Execute SQL:"+err.getMessage()); + return false; + } + return true; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/OviphoneDAO.java b/src/main/java/com/techsor/datacenter/sender/dao/OviphoneDAO.java new file mode 100644 index 0000000..591de2b --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/OviphoneDAO.java @@ -0,0 +1,133 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.BleHistoryEntity; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +@Component +public class OviphoneDAO { + @Resource + private JdbcTemplate jdbcTemplate ; + private static final Logger logger = LoggerFactory.getLogger(OviphoneDAO.class); + + /** + * 添加蓝牙人员进出记录 + * @param gatewayMAC + * @param bleMAC + * @param status + * @return + */ + public Boolean addRecord(String gatewayMAC, String bleMAC,int status,String ts) { + String sql = "INSERT INTO ble_history(ble_gateway_mac,ble_bluetooth_mac,ts,status) VALUES ('?1','?2','?3',?4)"; + sql = sql.replace("?1",gatewayMAC); + sql = sql.replace("?2",bleMAC); + sql = sql.replace("?3",ts); + sql = sql.replace("?4",status+""); + + logger.debug("Execute SQL: {}",sql); + Integer rows = this.jdbcTemplate.update(sql); + if (rows>0){ + return true; + }else{ + return false; + } + } + + /** + * 根据IMEI获取最新的IMEI记录 + * @param IMEI + * @return + */ + public List getLatestByIMEI(String IMEI, String ts) { + //上一条不是当前时间戳的数据,避免误判的情况 + String sql = "SELECT * FROM ble_history WHERE ble_bluetooth_mac='?1' and ts!='?2' order by id desc LIMIT 1 "; + sql = sql.replace("?1",IMEI); + sql = sql.replace("?2",ts); + + + logger.debug("Execute SQL: {}",sql); + + return getBleHistoryEntities(sql); + } + + /** + * 根据IMEI获取最新的IMEI记录 + * @param IMEI + * @return + */ + public List getLatestInByIMEI(String IMEI,String ts) { + //上一条不是当前时间戳的数据,避免误判的情况 + String sql = "SELECT * FROM ble_history WHERE ble_bluetooth_mac='?1' and ts!='?2' and status=1 order by id desc LIMIT 1 "; + sql = sql.replace("?1",IMEI); + sql = sql.replace("?2",ts); + + + logger.debug("Execute SQL: {}",sql); + + return getBleHistoryEntities(sql); + } + + @NotNull + private List getBleHistoryEntities(String sql) { + List bleHistoryList = jdbcTemplate.query(sql, + (rs, rowNum) -> getBleHistoryEntity(rs)); + + return bleHistoryList; + } + + @NotNull + private BleHistoryEntity getBleHistoryEntity(ResultSet rs) throws SQLException { + BleHistoryEntity item = new BleHistoryEntity(); + item.setId(rs.getInt("id")); + item.setBleGatewayMac(rs.getString("ble_gateway_mac")); + item.setBleBluetoothMac(rs.getString("ble_bluetooth_mac")); + item.setTs(rs.getString("ts")); + item.setStatus(rs.getInt("status")); + return item; + } + + /** + * 获取待发送的数据列表(30s前未发送的数据) + * @return + */ + public List getSendingList() { + //上一条不是当前时间戳的数据,避免误判的情况 + String sql = "SELECT * FROM ble_history WHERE ts bleHistoryList = jdbcTemplate.query(sql, + (rs, rowNum) -> getBleHistoryEntity(rs)); + + return bleHistoryList; + } + + /** + * 删除已经发送的数据 + * @return + */ + public Boolean deleteById(int id) { + //上一条不是当前时间戳的数据,避免误判的情况 + String sql = "UPDATE ble_history SET is_deleted=1 WHERE id="+id; + logger.debug("Execute SQL: {}",sql); + + Integer rs = jdbcTemplate.update(sql); + if (rs>0){ + return true; + }else{ + return false; + } + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/SkyDAO.java b/src/main/java/com/techsor/datacenter/sender/dao/SkyDAO.java new file mode 100644 index 0000000..1de105f --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/SkyDAO.java @@ -0,0 +1,64 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.SkyEnvEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; +@Component +public class SkyDAO { + @Resource + private JdbcTemplate jdbcTemplate ; + private static final Logger logger = LoggerFactory.getLogger(OviphoneDAO.class); + + /** + * 添加sky传感器雨量记录 + * @param deviceID + * @param rainFall + * @return + */ + public Boolean addRainFallRecord(String deviceID, String rainFall) { + + String sql = "INSERT INTO sky_env_history(device_id,rain_fall,ts) VALUES ('?1',?2,'?3')"; + sql = sql.replace("?1",deviceID); + sql = sql.replace("?2",rainFall); + sql = sql.replace("?3",System.currentTimeMillis()/1000+""); + + logger.debug("Execute SQL: {}",sql); + Integer rows = this.jdbcTemplate.update(sql); + if (rows>0){ + return true; + }else{ + return false; + } + } + + /** + * 获取最新雨量信息 + * @param deviceID + * @return + */ + // @Cacheable(value = "SkyDao::getLatestRainFallRecord",key = "#deviceID") + public List getLatestRainFallRecord(String deviceID) { + + String sql = "SELECT * from sky_env_history where device_id='"+deviceID+"' order by id desc limit 1"; + + logger.debug("Execute SQL: {}",sql); + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + SkyEnvEntity item = new SkyEnvEntity(); + item.setId(rs.getInt("id")); + item.setDeviceId(rs.getString("device_id")); + item.setRainFall(rs.getDouble("rain_fall")); + item.setTs(rs.getString("ts")); + return item; + }); + + return dataList; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/TargetForwardConfigDao.java b/src/main/java/com/techsor/datacenter/sender/dao/TargetForwardConfigDao.java new file mode 100644 index 0000000..8f41f6d --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/TargetForwardConfigDao.java @@ -0,0 +1,35 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.vo.TargetForwardConfigVO; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Component +public class TargetForwardConfigDao { + + @Resource + private JdbcTemplate jdbcTemplate; + public List selectTargetForwardConfigVOById(Long id) { + if (Objects.isNull(id)){ + return null; + } + String sql="select * from `target_forward_config` where id=%d and flag=0"; + sql=String.format(sql,id); + List targetForwardConfigVOList=new ArrayList<>(); + targetForwardConfigVOList=this.jdbcTemplate.query(sql,(rs, rowNum) -> { + TargetForwardConfigVO item = new TargetForwardConfigVO(); + item.setId(rs.getLong("id")); + item.setName(rs.getString("name")); + item.setTargetForwardCode(rs.getString("target_forward_code")); + item.setTargetForwardType(rs.getString("target_forward_type")); + item.setTargetForwardParams(rs.getString("target_forward_params")); + return item; + }); + return targetForwardConfigVOList; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dao/ZETADeviceDAO.java b/src/main/java/com/techsor/datacenter/sender/dao/ZETADeviceDAO.java new file mode 100644 index 0000000..32f6671 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dao/ZETADeviceDAO.java @@ -0,0 +1,40 @@ +package com.techsor.datacenter.sender.dao; + +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Component +public class ZETADeviceDAO { + + @Resource + private JdbcTemplate jdbcTemplate ; + + + + //根据deviceId查询设备列表 + @Cacheable(value = "ZETADeviceDAO::queryDeviceByDeviceID", key = "#deviceId") + public List queryDeviceByDeviceID(String deviceId) { + String sql = "SELECT * FROM device_info WHERE device_id='"+deviceId+"' and flag!=1"; + + return getDeviceEntities(sql); + } + + //获取设备列表基础查询方法 + private List getDeviceEntities(String sql) { + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setId(rs.getInt("id")); + item.setDeviceId(rs.getString("device_id")); + item.setTypeId(rs.getInt("type_id")); + return item; + }); + return dataList; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dto/DeviceAlertInfo.java b/src/main/java/com/techsor/datacenter/sender/dto/DeviceAlertInfo.java new file mode 100644 index 0000000..d9be41d --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dto/DeviceAlertInfo.java @@ -0,0 +1,46 @@ +package com.techsor.datacenter.sender.dto; + + +import lombok.Data; + +@Data +public class DeviceAlertInfo { + + private Integer id; + private Long deviceConfigId; + private String alertName; + private String level; + private String contents; + private String type; + private String targetId; + private String problemReportCategoryId; + + private String buildingId; + + private String buildingCode; + + private Long companyId; + + private String tmplName; + + + private String message; + + private Integer flag; + + private String templateDefs; + + private String forwardType; + + private String alertCancelTemplateDefs; + + private String alertCancelMessage; + + private String alertTitle; + + private String alertCancelTitle; + + private String alertRecipientMail; + + private String alertRecipientSms; +} diff --git a/src/main/java/com/techsor/datacenter/sender/dto/DeviceInfoVO.java b/src/main/java/com/techsor/datacenter/sender/dto/DeviceInfoVO.java new file mode 100644 index 0000000..21cd54c --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dto/DeviceInfoVO.java @@ -0,0 +1,50 @@ +package com.techsor.datacenter.sender.dto; + +import lombok.Data; + +/** +* @author Mr.Jiang +* @time 2022年7月21日 下午8:50:31 +*/ +@Data +public class DeviceInfoVO{ + + private String deviceId; + + private String deviceSn; + + private String deviceName; + +// private String assetSymbol; + + private String latestRawData; + + private Long latestDataTime; + + private String typeUnit; + + private String dataValue; + +// private String alertTitle; + +// private String alertLevel; + +// private String alertLevelName; + +// private String alertTypeName; + + private String buildingName; + + private String floorName; + + private String spaceName; + + private String assetName; + + private String alertStatus; + + private String category; + + private String monitoringPointName; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dto/RedisAlarmDTO.java b/src/main/java/com/techsor/datacenter/sender/dto/RedisAlarmDTO.java new file mode 100644 index 0000000..6661cab --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dto/RedisAlarmDTO.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.dto; + +import lombok.Data; + +@Data +public class RedisAlarmDTO { + + private String deviceId; + private Long problemReportId; + + private String status; + + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/dto/RedisCommandReceiverDTO.java b/src/main/java/com/techsor/datacenter/sender/dto/RedisCommandReceiverDTO.java new file mode 100644 index 0000000..42aa6ad --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dto/RedisCommandReceiverDTO.java @@ -0,0 +1,23 @@ +package com.techsor.datacenter.sender.dto; + +import lombok.Data; + +import java.util.List; + +/**** + * 开关,commandType为类型 + * commandType=='notification',则是通知处理mqtt相关信息 + * commandType=='switch',则是通知处理redis开关,则value为on,off + * ***/ +@Data +public class RedisCommandReceiverDTO { + + + private String commandType; + + private String value; + + private String state; + + private List params; +} diff --git a/src/main/java/com/techsor/datacenter/sender/dto/VariableBoundsDTO.java b/src/main/java/com/techsor/datacenter/sender/dto/VariableBoundsDTO.java new file mode 100644 index 0000000..cbaa26f --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dto/VariableBoundsDTO.java @@ -0,0 +1,40 @@ +package com.techsor.datacenter.sender.dto; + + +import lombok.Data; + +@Data +public class VariableBoundsDTO { + String variableName; + Double lowerBound = null; // 初始化为 null 表示尚未设置 + Double upperBound = null; // 初始化为 null 表示尚未设置 + + VariableExprBoundsDTO variableMaxExprBoundsDTO; + + VariableExprBoundsDTO variableMinExprBoundsDTO; + public VariableBoundsDTO(String variableName) { + this.variableName = variableName; + } + + public VariableBoundsDTO(String variableName, Double lowerBound, Double upperBound) { + this.variableName = variableName; + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + public void updateUpperBound(Double newUpperBound) { + if (upperBound == null || newUpperBound < upperBound) { + upperBound = newUpperBound; + } + } + + public void updateLowerBound(Double newLowerBound) { + if (lowerBound == null || newLowerBound > lowerBound) { + lowerBound = newLowerBound; + } + } + + @Override + public String toString() { + return variableName + ": LowerBound = " + lowerBound + ", UpperBound = " + upperBound; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/dto/VariableExprBoundsDTO.java b/src/main/java/com/techsor/datacenter/sender/dto/VariableExprBoundsDTO.java new file mode 100644 index 0000000..6105d38 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/dto/VariableExprBoundsDTO.java @@ -0,0 +1,14 @@ +package com.techsor.datacenter.sender.dto; + +import lombok.Data; + +@Data +public class VariableExprBoundsDTO { + + String variableName; + + Integer value = null; // 初始化为 null 表示尚未设置 + + + private String operator; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/AlertHistoryDTO.java b/src/main/java/com/techsor/datacenter/sender/entitiy/AlertHistoryDTO.java new file mode 100644 index 0000000..a3a8c3d --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/AlertHistoryDTO.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +@Data +public class AlertHistoryDTO { + private Long id; + private String deviceId; + private Long receiveTs; + private Integer confirmStatus; + private Integer handleStatus; + private Integer alertStatus; + private Integer retainAlert; +} + diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/AuthEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/AuthEntity.java new file mode 100644 index 0000000..83ee1e1 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/AuthEntity.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class AuthEntity implements Serializable { + private String subscription_id; + private String signature; + private String code; + private String version; + private Integer timestamp; + +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/BleHistoryEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/BleHistoryEntity.java new file mode 100644 index 0000000..5e95f9b --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/BleHistoryEntity.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class BleHistoryEntity implements Serializable { + private int id; + private String bleGatewayMac; + private String bleBluetoothMac; + private String ts; + private int status; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/ComplexTime.java b/src/main/java/com/techsor/datacenter/sender/entitiy/ComplexTime.java new file mode 100644 index 0000000..2c94a2f --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/ComplexTime.java @@ -0,0 +1,19 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +@Data +public class ComplexTime { + + private String dateKey; + private String previousDateKey; + private String dateHKey; + private String dateHMKey; + private int yearKey; + private int monthKey; + private int dayKey; + private int hourKey; + private int minuteKey; + private int secondKey; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DBMResponse.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DBMResponse.java new file mode 100644 index 0000000..6da5995 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DBMResponse.java @@ -0,0 +1,10 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +@Data +public class DBMResponse { + private String msg; + private String code; + private Boolean flag; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DataSrcConfigInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DataSrcConfigInfoEntity.java new file mode 100644 index 0000000..62ff540 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DataSrcConfigInfoEntity.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class DataSrcConfigInfoEntity implements Serializable { + + private String code; + + private String name; + + private String methodType; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DeltaClientCacheEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DeltaClientCacheEntity.java new file mode 100644 index 0000000..3c9c3f6 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DeltaClientCacheEntity.java @@ -0,0 +1,13 @@ +package com.techsor.datacenter.sender.entitiy; + +import com.techsor.datacenter.sender.entitiy.delta.DeltaCacheEntity; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; + +@Data +public class DeltaClientCacheEntity implements Serializable { + String clientId; + ArrayList deviceInfoList; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceAlertConfigEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceAlertConfigEntity.java new file mode 100644 index 0000000..fcc6ccb --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceAlertConfigEntity.java @@ -0,0 +1,48 @@ +package com.techsor.datacenter.sender.entitiy; + + +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +/*** + * id int auto_increment + * primary key, + * device_config_id bigint not null comment '设备类别配置ID', + * alert_name varchar(50) null comment '设备告警名称', + * level varchar(20) null comment '告警级别', + * contents varchar(200) null comment '告警内容', + * type varchar(100) null comment '告警类型', + * message varchar(200) null comment '告警信息', + * flag int default 0 null comment '0 开启,1关闭' + * + *problem_report_category_id + * ***/ +@Data +public class DeviceAlertConfigEntity implements Serializable { + + private Integer id; + private Long deviceConfigId; + private String alertName; + private String level; + private String contents; + private String type; + private String message; + private String alertCancelMessage; + private String alertTitle; + private String alertCancelTitle; + private Integer flag; + + private String targetId; + + private String problemReportCategoryId; + + private String forwardType; + + private String buildingId; + + private String buildingCode; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceAlertTemplateEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceAlertTemplateEntity.java new file mode 100644 index 0000000..4810720 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceAlertTemplateEntity.java @@ -0,0 +1,56 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +/*** + * ( + * id bigint auto_increment + * primary key, + * company_id bigint null, + * tmpl_name varchar(100) null, + * level varchar(20) null comment '告警级别', + * message varchar(999) null comment '告警信息', + * flag int default 0 null comment '0 开启,1关闭', + * template_defs varchar(999) null comment '模板定义', + * forward_type varchar(20) null comment '转发类型api, mail, sms', + * alert_cancel_template_defs varchar(999) default '' null comment '告警解除模板定义', + * alert_cancel_message varchar(999) default '' null comment '告警解除模板初步填充后内容', + * alert_title varchar(999) null, + * alert_cancel_title varchar(999) null, + * alert_recipient_mail varchar(1000) null comment '邮件通知对象', + * alert_recipient_sms varchar(1000) null comment '短信通知对象' + * ) + * + * */ +@Data +public class DeviceAlertTemplateEntity { + + private Long id; + + private Long companyId; + + private String tmplName; + + private String level; + + private String message; + + private Integer flag; + + private String templateDefs; + + private String forwardType; + + private String alertCancelTemplateDefs; + + private String alertCancelMessage; + + private String alertTitle; + + private String alertCancelTitle; + + private String alertRecipientMail; + + private String alertRecipientSms; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceConfigInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceConfigInfoEntity.java new file mode 100644 index 0000000..21a7ed4 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceConfigInfoEntity.java @@ -0,0 +1,57 @@ +package com.techsor.datacenter.sender.entitiy; + + +import lombok.Data; + +import java.io.Serializable; + +/** + * create table type + * ( + * id int auto_increment + * primary key, + * name varchar(45) null comment '设备ID', + * description varchar(999) null comment '备注', + * device_type varchar(200) null comment '设备类别', + * device_data_src varchar(200) null comment '设备数据来源', + * origin_json_format varchar(2000) null comment '原始数据来源', + * expression_map varchar(200) null comment '表达式映射', + * expression_variable_map varchar(500) null comment '表达式变量map', + * target_json_format varchar(2000) null comment '转发数据格式', + * target_forward_id bigint null comment '转发目标ID', + * target_foward_code varchar(100) null comment '转发识别代码', + * created_by bigint null comment '创建人', + * created_timestamp timestamp null comment '创建时间', + * updated_by bigint null, + * updated_timestamp timestamp null comment '创建时间', + * company_id bigint null + * ) + * comment '设备类别及备注,帮助识别设备类型和解析方式'; + * + * **/ + +@Data +public class DeviceConfigInfoEntity implements Serializable { + + private Integer id; + + private String name; + + private String description; + + private String deviceType; + + private String deviceDataSrc; + + private String originJsonFormat; + + private String expressionMap; + + private String expressionVariableMap; + + private String targetJsonFormat; + + private Long targetForwardId; + + private String targetFowardCode; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceEntity.java new file mode 100644 index 0000000..d124555 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceEntity.java @@ -0,0 +1,74 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + + +/** + * ( + * id int auto_increment + * primary key, + * device_id varchar(45) not null, + * device_sn varchar(99) null comment '用于记录SN,当SN与deviceId不同时使用', + * type_id int null, + * wsclient_id int null, + * space_id bigint null comment '空间ID', + * device_name varchar(200) null comment '设备名称', + * remark bigint null comment '备注信息', + * building_id bigint null comment '楼宇ID', + * assset_id bigint null comment '资产ID', + * flag int null, + * company_id bigint null comment '公司ID', + * created_by bigint null, + * created_timestamp timestamp null, + * updated_by bigint null, + * updated_timestamp bigint null, + * project_id bigint null comment '项目ID', + * floor_id bigint null comment '楼宇ID', + * constraint device_id_UNIQUE + * unique (device_id) + * ) + * + * */ +@Data +public class DeviceEntity implements Serializable { + + private Integer id; + + private String deviceId; + + private String deviceSN; + + private int typeId; + + private String wsClientId; + + private Long spaceId; + + private Long floorId; + + private String deviceName; + + private String remark; + + private Long buildingId; + + private Long assetId; + + private Long projectId; + + private int flag; + + private Long companyId; + + private Long createdBy; + + private Long createdTimestamp; + + private Long updatedBy; + + private Long updatedTimestamp; + + private Integer retainAlert; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceForwardConfigEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceForwardConfigEntity.java new file mode 100644 index 0000000..4e75f87 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceForwardConfigEntity.java @@ -0,0 +1,17 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +@Data +public class DeviceForwardConfigEntity implements java.io.Serializable{ + + + private Long id; + + + private Long deviceId; + + + private Long targetForwardId; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceInfoEntity.java new file mode 100644 index 0000000..0b5e61c --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DeviceInfoEntity.java @@ -0,0 +1,11 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class DeviceInfoEntity implements Serializable { + private String device_name; + private String device_class; +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DynamoAlertDBEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DynamoAlertDBEntity.java new file mode 100644 index 0000000..fdb170c --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DynamoAlertDBEntity.java @@ -0,0 +1,89 @@ +package com.techsor.datacenter.sender.entitiy; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; +import lombok.Data; + +import java.io.Serializable; +import java.util.UUID; + + +@Data +public class DynamoAlertDBEntity implements Serializable { + + @DynamoDBHashKey + @DynamoDBAutoGeneratedKey() + private UUID hashId; + + private Integer id; + + //0:MQTT,1:HTTP + private String methodType; + + private String deviceId; + + private String platformIdentifyId; + + private String deviceTypeName; + + private Integer alertLevel; + + private String alertLevelName; + + private String alertContent; + + private String alertCancelContent; + + private String alertTitle; + + private String rawData; + + private String status; + + private String messageId; + + private String buildingInfo; + + private String floorInfo; + + private String spaceInfo; + + private String equipmentInfo; + + private String projectInfo; + + private Long receive_ts; + + private String ts; + + private String company; //zifisense:纵行, nbi:农博, oviphone:oviphone + + private String dateKey; //日期,格式:yyyy-MM-dd + + private String yearKey; + + private String monthKey; + + private String dayKey; + + //针对METCOM的字段 + private String srcType; + + + private String needTransfer; + + private String targetId; + + private String problemReportCategoryId; + + private String forwardType; + + private String buildingId; + + private String buildingCode; + + //告警模板ID + private String alertTemplateIds; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/DynamodbEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/DynamodbEntity.java new file mode 100644 index 0000000..e20b7f7 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/DynamodbEntity.java @@ -0,0 +1,140 @@ +package com.techsor.datacenter.sender.entitiy; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.UUID; + + + +//{ +// "deviceId": "ffff0001", +// "platformIdentifyId": "ABCDEFFFF", +// "deviceTypeName": "漏水传感器", +// "alertLevel": 1, +// "alertContent": "报警啦,快去看", +// "rawData": "{\"humidity\":11}", +// "status": "normal", +// "messageId": "N111111111111111111111111111111", +// "buildingInfo": { +// "id": "A001", +// "name": "building-1" +// }, +// "floorInfo": { +// "id": "A001_2F", +// "name": "2階" +// }, +// "spaceInfo": { +// "id": "A001_2F_0005", +// "name": "room-003" +// }, +// "equipmentInfo": { +// "id": "A001_2F_0005", +// "name": "equip-003" +// }, +// "projectInfo": { +// "id": "pj001", +// "name": "projectname" +// }, +// "receive_ts": 1689398395000 +//} + + +@Data +public class DynamodbEntity implements Serializable { + + @DynamoDBHashKey + @DynamoDBAutoGeneratedKey() + private UUID hashId; + + private Integer id; + + private Long companyId; + + //0:MQTT,1:HTTP + private String methodType; + + private String deviceId; + + private String platformIdentifyId; + + private String deviceTypeName; + + private Integer alertLevel; + + private String alertLevelName; + + private String alertContent; + + private String alertTypeName; + + private String alertTitle; + + private String alertCancelTitle; + + private String alertCancelContent; + + private String rawData; + + private String status; + + private String messageId; + + private String buildingInfo; + + private String floorInfo; + + private String spaceInfo; + + private String equipmentInfo; + + private String projectInfo; + + private Long receive_ts; + + private String ts; + + private String company; //zifisense:纵行, nbi:农博, oviphone:oviphone + + private String dateKey; //日期,格式:yyyy-MM-dd + + private String yearKey; + + private String monthKey; + + private String dayKey; + + //针对METCOM的字段 + private String srcType; + + + private String needTransfer; + + private String targetId; + + private String problemReportCategoryId; + + private String forwardType; + + private String buildingId; + + private String buildingCode; + + //告警模板ID + private String alertTemplateIds; + + + private List forwardTypeList; + + private String category; + + private Long dbBuildingId; + + private Integer retainAlert; + + private Integer typeId; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/FacilityInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/FacilityInfoEntity.java new file mode 100644 index 0000000..9a0aefd --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/FacilityInfoEntity.java @@ -0,0 +1,12 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class FacilityInfoEntity implements Serializable { + private String facility_id; + private String facility_maker; + private String facility_mode; +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/HistoryEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/HistoryEntity.java new file mode 100644 index 0000000..1ed5cde --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/HistoryEntity.java @@ -0,0 +1,11 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class HistoryEntity implements Serializable { + private Integer id; + private String content; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/HttpResponse.java b/src/main/java/com/techsor/datacenter/sender/entitiy/HttpResponse.java new file mode 100644 index 0000000..ae52d00 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/HttpResponse.java @@ -0,0 +1,9 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +@Data +public class HttpResponse { + private int code = -1; + private String msg = null; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/JsonResponse.java b/src/main/java/com/techsor/datacenter/sender/entitiy/JsonResponse.java new file mode 100644 index 0000000..39923d6 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/JsonResponse.java @@ -0,0 +1,57 @@ +package com.techsor.datacenter.sender.entitiy; + +public class JsonResponse { + private int code; + private Object data; + private String msg; + + public JsonResponse() { + } + + public JsonResponse(int code, Object data) { + this.code = code; + this.data = data; + } + + public JsonResponse(int code, Object data, String msg) { + this.code = code; + this.data = data; + this.msg = msg; + } + + public static JsonResponse buildSuccess(Object data) { + return new JsonResponse(200, data); + } + + public static JsonResponse buildError(String msg) { + return new JsonResponse(500, "", msg); + } + + public static JsonResponse buildError(int code, String msg) { + return new JsonResponse(code, "", msg); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/LocationInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/LocationInfoEntity.java new file mode 100644 index 0000000..80c5806 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/LocationInfoEntity.java @@ -0,0 +1,10 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class LocationInfoEntity implements Serializable { + private String location_address; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/MqttConfigEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/MqttConfigEntity.java new file mode 100644 index 0000000..2f34c0a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/MqttConfigEntity.java @@ -0,0 +1,22 @@ +package com.techsor.datacenter.sender.entitiy; + + +import lombok.Data; + +import java.io.Serializable; + + +@Data +public class MqttConfigEntity implements Serializable { + + private Long mqttId; + private Long companyId; + private String companyName; + private String mqttName; + private String mqttUrl; + private String mqttUsername; + private String mqttPassword; + private String mqttTopic; + private String apikey; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/MsgParamEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/MsgParamEntity.java new file mode 100644 index 0000000..9e85a63 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/MsgParamEntity.java @@ -0,0 +1,14 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class MsgParamEntity implements Serializable { + private String subCmd; + private String subType; + private String msUid; + private String data; + private String dataEncrypt; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/NittanEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/NittanEntity.java new file mode 100644 index 0000000..3470e69 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/NittanEntity.java @@ -0,0 +1,21 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class NittanEntity implements Serializable { + private AuthEntity auth; + private String cmd; + private FacilityInfoEntity facility_info; + private List notice_info; +} + + + + + + + diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/NoticeInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/NoticeInfoEntity.java new file mode 100644 index 0000000..56623ae --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/NoticeInfoEntity.java @@ -0,0 +1,14 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class NoticeInfoEntity implements Serializable { + private String notice_type; + private String notice_level; + private String notice_status; + private LocationInfoEntity location_info; + private DeviceInfoEntity device_info; +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/OcrHistoryEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/OcrHistoryEntity.java new file mode 100644 index 0000000..bb03ed3 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/OcrHistoryEntity.java @@ -0,0 +1,13 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class OcrHistoryEntity implements Serializable { + private int id; + private double value; + private String deviceSN; + private String ts; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/ProcessRAWEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/ProcessRAWEntity.java new file mode 100644 index 0000000..f0f5555 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/ProcessRAWEntity.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ProcessRAWEntity implements Serializable { + + private Integer id; + private String content; + private String ts; + // 实际为deviceId + private String dataSrcCode; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/RAWEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/RAWEntity.java new file mode 100644 index 0000000..0280a9b --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/RAWEntity.java @@ -0,0 +1,13 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class RAWEntity implements Serializable { + private Integer id; + private String content; + private String ts; + private String company; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/ResultDeviceIdEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/ResultDeviceIdEntity.java new file mode 100644 index 0000000..a269074 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/ResultDeviceIdEntity.java @@ -0,0 +1,10 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +@Data +public class ResultDeviceIdEntity { + private String deviceId; + private String result; + private String ts; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/SendEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/SendEntity.java new file mode 100644 index 0000000..6cdfcca --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/SendEntity.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.entitiy; + +import java.io.Serializable; + +public class SendEntity implements Serializable { + private String content; + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/SkyEnvEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/SkyEnvEntity.java new file mode 100644 index 0000000..bbbf903 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/SkyEnvEntity.java @@ -0,0 +1,13 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class SkyEnvEntity implements Serializable { + private int id; + private String deviceId; + private Double rainFall; + private String ts; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/StatisticsAccumulateInfo.java b/src/main/java/com/techsor/datacenter/sender/entitiy/StatisticsAccumulateInfo.java new file mode 100644 index 0000000..8a48811 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/StatisticsAccumulateInfo.java @@ -0,0 +1,11 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +@Data +public class StatisticsAccumulateInfo extends ComplexTime{ + + private Object value; + private long uploadAt; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/StatisticsMeasureInfo.java b/src/main/java/com/techsor/datacenter/sender/entitiy/StatisticsMeasureInfo.java new file mode 100644 index 0000000..ed37129 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/StatisticsMeasureInfo.java @@ -0,0 +1,13 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +@Data +public class StatisticsMeasureInfo extends ComplexTime{ + + private Object value; + private Object maxValue; + private Object minValue; + private long uploadAt; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/UdfAssetInfo.java b/src/main/java/com/techsor/datacenter/sender/entitiy/UdfAssetInfo.java new file mode 100644 index 0000000..67c61eb --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/UdfAssetInfo.java @@ -0,0 +1,28 @@ +package com.techsor.datacenter.sender.entitiy; + + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class UdfAssetInfo implements Serializable { + + private String id; + private String assetName; + private String assetClassBig; + private String assetClassMedium; + private String assetClassSmall; + private String assetSymbol; + private String assetManufacturer; + private String assetModel; + private String assetManufacturingDate; + private String assetSpec1; + private String assetSpec2; + private String assetSpec3; + private String assetSpec4; + private String assetSpec5; + private String assetSpec6; + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/UdfCommonInfo.java b/src/main/java/com/techsor/datacenter/sender/entitiy/UdfCommonInfo.java new file mode 100644 index 0000000..1ec5e0f --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/UdfCommonInfo.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.entitiy; + + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class UdfCommonInfo implements Serializable { + + private String id; + + private String name; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/UdfCommonJsonInfo.java b/src/main/java/com/techsor/datacenter/sender/entitiy/UdfCommonJsonInfo.java new file mode 100644 index 0000000..ba146f0 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/UdfCommonJsonInfo.java @@ -0,0 +1,13 @@ +package com.techsor.datacenter.sender.entitiy; + + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class UdfCommonJsonInfo implements Serializable { + + private String data; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/ZETABaseEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/ZETABaseEntity.java new file mode 100644 index 0000000..66a3a79 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/ZETABaseEntity.java @@ -0,0 +1,21 @@ +package com.techsor.datacenter.sender.entitiy; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ZETABaseEntity implements Serializable { + private String msgDirect; + private String msgPriority; + private String msgType; + private Integer msgId; + private Long apTime; + private String msgEncrypt; + private String msgUid; + private String apUid; + private String msgCmd; + private MsgParamEntity msgParam; +} + + diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/bastatus/BaStatusEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/bastatus/BaStatusEntity.java new file mode 100644 index 0000000..8a6216d --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/bastatus/BaStatusEntity.java @@ -0,0 +1,22 @@ +package com.techsor.datacenter.sender.entitiy.bastatus; + + +import lombok.Data; + +import java.io.Serializable; + + +@Data +public class BaStatusEntity implements Serializable { + + private int id; + private int deviceInfoId; + private int isRunning; + private String latest_ts; + private Long continuousRunningTime; + private Long aggregatedRunningTime; + private int runningCount; + private Long lastStartTime; + private Long lastStopTime; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/bastatus/BaStatusThirdReqEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/bastatus/BaStatusThirdReqEntity.java new file mode 100644 index 0000000..e78f617 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/bastatus/BaStatusThirdReqEntity.java @@ -0,0 +1,17 @@ +package com.techsor.datacenter.sender.entitiy.bastatus; + + +import lombok.Data; + + +@Data +public class BaStatusThirdReqEntity { + + private Integer targetId; + private Integer is_running; + private String occured_at; + private String continuous_running_time; + private String aggregated_running_time; + private int running_count; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/bluetooth/BlueToothDeviceEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/bluetooth/BlueToothDeviceEntity.java new file mode 100644 index 0000000..1b9e2da --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/bluetooth/BlueToothDeviceEntity.java @@ -0,0 +1,11 @@ +package com.techsor.datacenter.sender.entitiy.bluetooth; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class BlueToothDeviceEntity implements Serializable { + private String deviceId; + private String flag; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/company/CompanyEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/company/CompanyEntity.java new file mode 100644 index 0000000..21272e9 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/company/CompanyEntity.java @@ -0,0 +1,22 @@ +package com.techsor.datacenter.sender.entitiy.company; + + +import lombok.Data; + +import java.io.Serializable; + + +@Data +public class CompanyEntity implements Serializable { + + private Long id; + + private Long parentId; + + private String companyName; + + private String bearerToken; + + private String thirdApiHost; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/delta/DeltaCacheEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/delta/DeltaCacheEntity.java new file mode 100644 index 0000000..34ce3d3 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/delta/DeltaCacheEntity.java @@ -0,0 +1,13 @@ +package com.techsor.datacenter.sender.entitiy.delta; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class DeltaCacheEntity implements Serializable { + private String deviceId; + private String deviceSN; + private Integer typeId; + private Object value; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/delta/DeltaNumEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/delta/DeltaNumEntity.java new file mode 100644 index 0000000..60ffaf5 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/delta/DeltaNumEntity.java @@ -0,0 +1,13 @@ +package com.techsor.datacenter.sender.entitiy.delta; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class DeltaNumEntity implements Serializable { + private String id; + private String type; + private String reportedAt; + private Object value; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIODataItemEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIODataItemEntity.java new file mode 100644 index 0000000..88025bf --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIODataItemEntity.java @@ -0,0 +1,22 @@ +package com.techsor.datacenter.sender.entitiy.kingio; + +import lombok.Data; + +@Data +public class KingIODataItemEntity { + + private String deviceName; + + private Object value; + + private String timestamp; + + private String quality; + + public KingIODataItemEntity(String name, Object value, String formattedTimestamp, String s) { + this.deviceName = name; + this.value = value; + this.timestamp = formattedTimestamp; + this.quality = s; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIODataModel.java b/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIODataModel.java new file mode 100644 index 0000000..99fce4a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIODataModel.java @@ -0,0 +1,24 @@ +package com.techsor.datacenter.sender.entitiy.kingio; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class KingIODataModel { + @JsonProperty("Sum") + private Integer sum; + @JsonProperty("Now") + private Integer now; + @JsonProperty("PNs") + private Map propertyNames; + @JsonProperty("PVs") + private Map propertyValues; + @JsonProperty("Objs") + private List> objects; + + // getters and setters +} + diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIODbmEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIODbmEntity.java new file mode 100644 index 0000000..5877f2f --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIODbmEntity.java @@ -0,0 +1,10 @@ +package com.techsor.datacenter.sender.entitiy.kingio; + +import lombok.Data; + +@Data +public class KingIODbmEntity { + String deviceId; + String content; + String ts; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIOObjectModel.java b/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIOObjectModel.java new file mode 100644 index 0000000..0205630 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/kingio/KingIOObjectModel.java @@ -0,0 +1,22 @@ +package com.techsor.datacenter.sender.entitiy.kingio; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.Map; + +@Data +public class KingIOObjectModel { + @JsonProperty("N") + private String name; + @JsonProperty("Q") + private Integer qualityStamp; + @JsonProperty("T") + private Long timestamp; + @JsonProperty("V") + private Object value; // 存储Obj的值 + private Map properties; + + // getters and setters +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/metcom/MetcomEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/metcom/MetcomEntity.java new file mode 100644 index 0000000..cae713a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/metcom/MetcomEntity.java @@ -0,0 +1,26 @@ +package com.techsor.datacenter.sender.entitiy.metcom; + +import lombok.Data; + +import java.io.Serializable; + +@Data +//{ +// “measured” : [計測時刻 (unix timestamp)], +// “spaceId” : “[DBM空間コード]”, +// “latitude” : [緯度(deg)], +// “longitude” : [経度(deg)], +// “floor” : [階数], +// “hae” : [楕円体高(m)], +// “hat” : [地上高(m)] +//} +public class MetcomEntity implements Serializable { + private String uuid; + private Long measured; + private String spaceId; + private double latitude; + private double longitude; + private int floor; + private float hae; + private float hat; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/metcom/MetcomRecordEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/metcom/MetcomRecordEntity.java new file mode 100644 index 0000000..af3333d --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/metcom/MetcomRecordEntity.java @@ -0,0 +1,12 @@ +package com.techsor.datacenter.sender.entitiy.metcom; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class MetcomRecordEntity implements Serializable { + private String uuid; + private String spaceId; + private String bleId; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/metcom/MetcomSpaceBleEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/metcom/MetcomSpaceBleEntity.java new file mode 100644 index 0000000..ac7daec --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/metcom/MetcomSpaceBleEntity.java @@ -0,0 +1,11 @@ +package com.techsor.datacenter.sender.entitiy.metcom; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class MetcomSpaceBleEntity implements Serializable { + private String spaceId; + private String bleId; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nbi/NbiData.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nbi/NbiData.java new file mode 100644 index 0000000..cbcc066 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nbi/NbiData.java @@ -0,0 +1,12 @@ +package com.techsor.datacenter.sender.entitiy.nbi; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class NbiData implements Serializable { + private String port; + private String name; + private float value; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nbi/NbiEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nbi/NbiEntity.java new file mode 100644 index 0000000..03b139a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nbi/NbiEntity.java @@ -0,0 +1,12 @@ +package com.techsor.datacenter.sender.entitiy.nbi; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class NbiEntity implements Serializable { + private NbiPayload payload; + private String type; + private String version; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nbi/NbiPayload.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nbi/NbiPayload.java new file mode 100644 index 0000000..6915a77 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nbi/NbiPayload.java @@ -0,0 +1,13 @@ +package com.techsor.datacenter.sender.entitiy.nbi; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class NbiPayload implements Serializable { + private List data; + private String device; + private Long timestamp; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/AuthEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/AuthEntity.java new file mode 100644 index 0000000..b59bb82 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/AuthEntity.java @@ -0,0 +1,15 @@ +package com.techsor.datacenter.sender.entitiy.nittan; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class AuthEntity implements Serializable { + private String subscription_id; + private String signature; + private String code; + private String version; + private Integer timestamp; + +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/DeviceInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/DeviceInfoEntity.java new file mode 100644 index 0000000..48b8f58 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/DeviceInfoEntity.java @@ -0,0 +1,11 @@ +package com.techsor.datacenter.sender.entitiy.nittan; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class DeviceInfoEntity implements Serializable { + private String device_name; + private String device_class; +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/FacilityInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/FacilityInfoEntity.java new file mode 100644 index 0000000..cb20f60 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/FacilityInfoEntity.java @@ -0,0 +1,12 @@ +package com.techsor.datacenter.sender.entitiy.nittan; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class FacilityInfoEntity implements Serializable { + private String facility_id; + private String facility_maker; + private String facility_mode; +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/LocationInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/LocationInfoEntity.java new file mode 100644 index 0000000..6ee4c63 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/LocationInfoEntity.java @@ -0,0 +1,10 @@ +package com.techsor.datacenter.sender.entitiy.nittan; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class LocationInfoEntity implements Serializable { + private String location_address; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/NittanEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/NittanEntity.java new file mode 100644 index 0000000..7cbda54 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/NittanEntity.java @@ -0,0 +1,21 @@ +package com.techsor.datacenter.sender.entitiy.nittan; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class NittanEntity implements Serializable { + private AuthEntity auth; + private String cmd; + private FacilityInfoEntity facility_info; + private List notice_info; +} + + + + + + + diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/NoticeInfoEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/NoticeInfoEntity.java new file mode 100644 index 0000000..60a22c0 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/NoticeInfoEntity.java @@ -0,0 +1,14 @@ +package com.techsor.datacenter.sender.entitiy.nittan; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class NoticeInfoEntity implements Serializable { + private String notice_type; + private String notice_level; + private String notice_status; + private LocationInfoEntity location_info; + private DeviceInfoEntity device_info; +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/OCREntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/OCREntity.java new file mode 100644 index 0000000..82f228a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/OCREntity.java @@ -0,0 +1,20 @@ +package com.techsor.datacenter.sender.entitiy.nittan; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class OCREntity implements Serializable { + private String message_type; + private OCRMessageEntity message; +} + + + + + + + + + diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/OCRMessageEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/OCRMessageEntity.java new file mode 100644 index 0000000..e6b2ba1 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/nittan/OCRMessageEntity.java @@ -0,0 +1,25 @@ +package com.techsor.datacenter.sender.entitiy.nittan; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class OCRMessageEntity implements Serializable { + private String alarm_type; + private String text; + private String device_id; + private String point_value; + private String meter_value; + private String datetime; +} + + + + + + + + + diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/st150/St150DataItem.java b/src/main/java/com/techsor/datacenter/sender/entitiy/st150/St150DataItem.java new file mode 100644 index 0000000..c560244 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/st150/St150DataItem.java @@ -0,0 +1,11 @@ +package com.techsor.datacenter.sender.entitiy.st150; + +import lombok.Data; + +// 嵌套类 +@Data +public class St150DataItem { + private String tag; // 标签字段 + private double value; // 值字段 + private int quality; // 质量字段 +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/st150/St150DataModel.java b/src/main/java/com/techsor/datacenter/sender/entitiy/st150/St150DataModel.java new file mode 100644 index 0000000..72e15f9 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/st150/St150DataModel.java @@ -0,0 +1,14 @@ +package com.techsor.datacenter.sender.entitiy.st150; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class St150DataModel { + private List d; // 嵌套的 DataItem 列表 + private String ts; // 时间戳字段 +} + diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/st150/St150DbmEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/st150/St150DbmEntity.java new file mode 100644 index 0000000..c857e01 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/st150/St150DbmEntity.java @@ -0,0 +1,10 @@ +package com.techsor.datacenter.sender.entitiy.st150; + +import lombok.Data; + +@Data +public class St150DbmEntity { + String deviceId; + String content; + String ts; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ProcessZAIoTRAWEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ProcessZAIoTRAWEntity.java new file mode 100644 index 0000000..6d8cafc --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ProcessZAIoTRAWEntity.java @@ -0,0 +1,17 @@ +package com.techsor.datacenter.sender.entitiy.zaiot; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class ProcessZAIoTRAWEntity implements Serializable { + private String content; + private String ts; + private String deviceId; + private String identify; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTAlarmDataEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTAlarmDataEntity.java new file mode 100644 index 0000000..49c6dd5 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTAlarmDataEntity.java @@ -0,0 +1,145 @@ +package com.techsor.datacenter.sender.entitiy.zaiot; + +import lombok.Data; + +@Data +public class ZAIoTAlarmDataEntity { + private String xRotationRMS_nameEn; + private double voltage_value; + private double xLubricationFactor_rawValue; + private String temperature_unit; + private double temperature_value; + private double xHarmonicsRMS_rawValue; + private String voltage_name; + private String voltage_unit; + private String ySpeedRMS_nameEn; + private String temperature_name; + private double zHarmonicsRMS_value; + private double yAccelerationRMS_rawValue; + private double xRotationRMS_value; + private String xSpeedRMS_unit; + private double xMeshRMS_value; + private double xShockRMS_value; + private String zSpeedRMS_nameJp; + private double temperature_rawValue; + private String zHarmonicsRMS_nameJp; + private double yHarmonicsRMS_rawValue; + private double xAccelerationRMS_rawValue; + private double ySpeedRMS_rawValue; + private String zShockRMS_nameEn; + private String xHarmonicsRMS_unit; + private double xHarmonicsRMS_value; + private String ySpeedRMS_name; + private String yMeshRMS_unit; + private String dataStatus; + private double zLubricationFactor_value; + private String yMeshRMS_name; + private String xSpeedRMS_name; + private String zMeshRMS_unit; + private double zRotationRMS_rawValue; + private String zMeshRMS_name; + private String temperature_nameEn; + private String xLubricationFactor_nameJp; + private double xSpeedRMS_value; + private String zHarmonicsRMS_unit; + private double yHarmonicsRMS_value; + private String xMeshRMS_name; + private String xHarmonicsRMS_nameEn; + private double zAccelerationRMS_rawValue; + private String zHarmonicsRMS_name; + private String xMeshRMS_nameEn; + private String xShockRMS_nameEn; + private String yRotationRMS_unit; + private String yLubricationFactor_name; + private double yLubricationFactor_value; + private String voltage_nameJp; + private String yRotationRMS_name; + private String ySpeedRMS_unit; + private String yLubricationFactor_nameEn; + private double zShockRMS_value; + private String yLubricationFactor_unit; + private String yShockRMS_nameJp; + private String yRotationRMS_nameJp; + private double xAccelerationRMS_value; + private double ySpeedRMS_value; + private String xHarmonicsRMS_name; + private double xMeshRMS_rawValue; + private String zAccelerationRMS_nameEn; + private double yMeshRMS_value; + private String xSpeedRMS_nameEn; + private String yMeshRMS_nameEn; + private double yAccelerationRMS_value; + private String zLubricationFactor_nameEn; + private String yAccelerationRMS_nameJp; + private double yShockRMS_rawValue; + private double zSpeedRMS_rawValue; + private String zSpeedRMS_nameEn; + private String xAccelerationRMS_nameEn; + private String zRotationRMS_nameEn; + private double yShockRMS_value; + private double zRotationRMS_value; + private String zMeshRMS_nameEn; + private double zSpeedRMS_value; + private String xLubricationFactor_nameEn; + private String zShockRMS_nameJp; + private double zAccelerationRMS_value; + private double yLubricationFactor_rawValue; + private String yShockRMS_unit; + private String yShockRMS_name; + private String zSpeedRMS_unit; + private String xHarmonicsRMS_nameJp; + private double xLubricationFactor_value; + private double voltage_rawValue; + private double zMeshRMS_value; + private String xRotationRMS_name; + private String xAccelerationRMS_name; + private String zSpeedRMS_name; + private String xRotationRMS_unit; + private String ySpeedRMS_nameJp; + private String xAccelerationRMS_unit; + private double yRotationRMS_value; + private double zShockRMS_rawValue; + private String yShockRMS_nameEn; + private String yHarmonicsRMS_nameEn; + private String xSpeedRMS_nameJp; + private double zMeshRMS_rawValue; + private String zHarmonicsRMS_nameEn; + private String yAccelerationRMS_name; + private String yAccelerationRMS_unit; + private String xRotationRMS_nameJp; + private String zLubricationFactor_nameJp; + private String yMeshRMS_nameJp; + private String zAccelerationRMS_name; + private String xShockRMS_name; + private String zShockRMS_unit; + private String zShockRMS_name; + private String yAccelerationRMS_nameEn; + private double yRotationRMS_rawValue; + private String xShockRMS_unit; + private String temperature_nameJp; + private String zRotationRMS_nameJp; + private String xAccelerationRMS_nameJp; + private String zAccelerationRMS_unit; + private double xSpeedRMS_rawValue; + private String yHarmonicsRMS_nameJp; + private String zLubricationFactor_unit; + private String xMeshRMS_nameJp; + private double xShockRMS_rawValue; + private String zLubricationFactor_name; + private String xShockRMS_nameJp; + private String zMeshRMS_nameJp; + private String xLubricationFactor_unit; + private String yRotationRMS_nameEn; + private String voltage_nameEn; + private double yMeshRMS_rawValue; + private String yLubricationFactor_nameJp; + private double xRotationRMS_rawValue; + private String yHarmonicsRMS_name; + private String yHarmonicsRMS_unit; + private String zRotationRMS_unit; + private String xLubricationFactor_name; + private double zHarmonicsRMS_rawValue; + private String zRotationRMS_name; + private String zAccelerationRMS_nameJp; + private double zLubricationFactor_rawValue; +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTBaseAlarmCancelEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTBaseAlarmCancelEntity.java new file mode 100644 index 0000000..c5b11f5 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTBaseAlarmCancelEntity.java @@ -0,0 +1,36 @@ +package com.techsor.datacenter.sender.entitiy.zaiot; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ZAIoTBaseAlarmCancelEntity implements Serializable{ + private Payload payload; + private Source source; + + @Data + public static class Payload { + private DataDetail data; + private String identify; + + @Data + public static class DataDetail { + private long alarmTime; + private long processingTime; + private String pointName; + private String deviceName; + private String companyName; + private Integer processSchema; + private String processContent; + private String msSn; + } + } + + @Data + public static class Source { + private long createTime; + private long pushTime; + private String topicName; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTBaseAlarmEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTBaseAlarmEntity.java new file mode 100644 index 0000000..2d89592 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTBaseAlarmEntity.java @@ -0,0 +1,36 @@ +package com.techsor.datacenter.sender.entitiy.zaiot; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ZAIoTBaseAlarmEntity implements Serializable{ + private Payload payload; + private Source source; + + @Data + public static class Payload { + private DataDetail data; + private String identify; + + @Data + public static class DataDetail { + private long upTime; + private String parsedData; + private String pointName; + private String companyName; + private String fault; + private Integer state; + private String deviceName; + private String msUid; + } + } + + @Data + public static class Source { + private long createTime; + private long pushTime; + private String topicName; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTBaseEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTBaseEntity.java new file mode 100644 index 0000000..0a8156e --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTBaseEntity.java @@ -0,0 +1,34 @@ +package com.techsor.datacenter.sender.entitiy.zaiot; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ZAIoTBaseEntity implements Serializable{ + private Payload payload; + private Source source; + + @Data + public static class Payload { + private DataDetail data; + private String identify; + + @Data + public static class DataDetail { + private long upTime; + private long apTime; + private String parsedData; + private String apUid; + private String rawData; + private String msUid; + } + } + + @Data + public static class Source { + private long createTime; + private long pushTime; + private String topicName; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTUpdataEntity.java b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTUpdataEntity.java new file mode 100644 index 0000000..5b93925 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/zaiot/ZAIoTUpdataEntity.java @@ -0,0 +1,149 @@ +package com.techsor.datacenter.sender.entitiy.zaiot; + +import lombok.Data; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Data +public class ZAIoTUpdataEntity { + private Parameter accelerationRms; + private String dataStatus; + private Parameter displPeakValue; + private Parameter envelopeHarmonicsFactor; + private Parameter envelopeHarmonicsFreq; + private Parameter envelopeRotationFactor; + private Parameter envelopeRotationFreq; + private Parameter harmonicsFreq; + private Parameter harmonicsRms; + private String header; + private Parameter highFreqRms; + private Parameter kurtosis; + private Parameter lowFreqRms; + private Parameter lubricationFactor; + private Parameter medianRms; + private Parameter meshRms; + private Parameter peakValue; + private Parameter rotationFreq; + private Parameter rotationRms; + private Parameter shockRms; + private Parameter skewness; + private Parameter speedRms; + private Parameter temperature; + private Parameter voltage; + + private static final Logger logger = LoggerFactory.getLogger(ZAIoTUpdataEntity.class); + + @Data + public static class Parameter { + private String name; + private String nameEn; + private String nameJp; + private double rawValue; + private String unit; + private double value; + } + + public static String generateMessage(ZAIoTUpdataEntity updataEntity) { + String dbmMsg = "[deviceId]_LowRms,[deviceId]_MedianRms,[deviceId]_VelocityRms,[deviceId]_AccelerationRms," + + "[deviceId]_RotationRms,[deviceId]_HarmonicsRms,[deviceId]_MeshRms,[deviceId]_ShockRms," + + "[deviceId]_Lubricationfactor,[deviceId]_EnvelopeRotationFactor,[deviceId]_EnvelopeHarmonicsFactor," + + "[deviceId]_RotationFrequency,[deviceId]_HarmonicsFrequency,[deviceId]_EnvelopeRotationFrequency," + + "[deviceId]_EnvelopeHarmonics Frequency,[deviceId]_HighRms,[deviceId]_DisplacementPeakToPeak," + + "[deviceId]_AccelerationPeakToPeak,[deviceId]_kurtosis,[deviceId]_skewness,[deviceId]_temperature,[deviceId]_Voltage"; + + String[] fields = dbmMsg.split(","); + + StringBuilder messageBuilder = new StringBuilder("{"); + for (String field : fields) { + String key = field; + String value = ""; + + try{ + switch (key) { + case "[deviceId]_LowRms": + value = String.valueOf(updataEntity.getLowFreqRms().getValue()); + break; + case "[deviceId]_MedianRms": + value = String.valueOf(updataEntity.getMedianRms().getValue()); + break; + case "[deviceId]_VelocityRms": + value = String.valueOf(updataEntity.getSpeedRms().getValue()); + break; + case "[deviceId]_AccelerationRms": + value = String.valueOf(updataEntity.getAccelerationRms().getValue()); + break; + case "[deviceId]_RotationRms": + value = String.valueOf(updataEntity.getRotationRms().getValue()); + break; + case "[deviceId]_HarmonicsRms": + value = String.valueOf(updataEntity.getHarmonicsRms().getValue()); + break; + case "[deviceId]_MeshRms": + value = String.valueOf(updataEntity.getMeshRms().getValue()); + break; + case "[deviceId]_ShockRms": + value = String.valueOf(updataEntity.getShockRms().getValue()); + break; + case "[deviceId]_Lubricationfactor": + value = String.valueOf(updataEntity.getLubricationFactor().getValue()); + break; + case "[deviceId]_EnvelopeRotationFactor": + value = String.valueOf(updataEntity.getEnvelopeRotationFactor().getValue()); + break; + case "[deviceId]_EnvelopeHarmonicsFactor": + value = String.valueOf(updataEntity.getEnvelopeHarmonicsFactor().getValue()); + break; + case "[deviceId]_RotationFrequency": + value = String.valueOf(updataEntity.getRotationFreq().getValue()); + break; + case "[deviceId]_HarmonicsFrequency": + value = String.valueOf(updataEntity.getHarmonicsFreq().getValue()); + break; + case "[deviceId]_EnvelopeRotationFrequency": + value = String.valueOf(updataEntity.getEnvelopeRotationFreq().getValue()); + break; + case "[deviceId]_EnvelopeHarmonics Frequency": + value = String.valueOf(updataEntity.getEnvelopeHarmonicsFreq().getValue()); + break; + case "[deviceId]_HighRms": + value = String.valueOf(updataEntity.getHighFreqRms().getValue()); + break; + case "[deviceId]_DisplacementPeakToPeak": + value = String.valueOf(updataEntity.getDisplPeakValue().getValue()); + break; + case "[deviceId]_AccelerationPeakToPeak": + value = String.valueOf(updataEntity.getPeakValue().getValue()); + break; + case "[deviceId]_kurtosis": + value = String.valueOf(updataEntity.getKurtosis().getValue()); + break; + case "[deviceId]_skewness": + value = String.valueOf(updataEntity.getSkewness().getValue()); + break; + case "[deviceId]_temperature": + value = String.valueOf(updataEntity.getTemperature().getValue()); + break; + case "[deviceId]_Voltage": + value = String.valueOf(updataEntity.getVoltage().getValue()); + break; + default: + value = "null"; + } + + messageBuilder.append("\"").append(key).append("\":").append(value).append(","); + } catch (Exception e) { + logger.error("Error processing field: " + key, e); + continue; + } + + } + + // 删除最后的逗号 + if (messageBuilder.charAt(messageBuilder.length() - 1) == ',') { + messageBuilder.deleteCharAt(messageBuilder.length() - 1); + } + + messageBuilder.append("}"); + return messageBuilder.toString(); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/exceptions/BusinessException.java b/src/main/java/com/techsor/datacenter/sender/exceptions/BusinessException.java new file mode 100644 index 0000000..9e93215 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/exceptions/BusinessException.java @@ -0,0 +1,9 @@ +package com.techsor.datacenter.sender.exceptions; + +public class BusinessException extends RuntimeException{ + + + public BusinessException(String errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/helpers/AngleHelper.java b/src/main/java/com/techsor/datacenter/sender/helpers/AngleHelper.java new file mode 100644 index 0000000..7078ad9 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/helpers/AngleHelper.java @@ -0,0 +1,48 @@ +package com.techsor.datacenter.sender.helpers; + +import static java.lang.Math.asin; +/** + * 倾斜传感器数据处理助手类。 + *

    + * 提供处理倾斜传感器原始数据和计算倾斜角度的静态方法。 + */ +public class AngleHelper { + /** + * 将倾斜传感器的原始数据字符串转换为双精度浮点数。 + *

    + * 如果原始数据以"0f"开头,则表示数据超过了正常范围,需要进行调整。 + * + * @param valueStr 倾斜传感器的原始数据字符串 + * @return 转换后的双精度浮点数 + */ + public static Double processValue(String valueStr){ + Double valueDou; + if (valueStr.startsWith("0f")){ + valueDou = Double.valueOf(Integer.valueOf(valueStr,16))-1048576; + }else{ + valueDou = Double.valueOf(Integer.valueOf(valueStr,16)); + } + return valueDou; + } + + /** + * 根据原始值和增益计算倾斜角度。 + *

    + * 根据传感器的原始值和增益参数,计算实际的倾斜角度。该方法适用于需要进行角度转换的场景。 + * + * @param value 倾斜传感器的原始值 + * @param gain 传感器的增益 + * @return 计算后的倾斜角度 + */ + public static Double processAngleValue(Double value,Double gain){ + Double result=0.0; + if (value=gain){ + result = 90-asin((gain-value+gain)/gain)*180/Math.PI+90; + }else if (value0){ + result = asin(value/gain)*180/Math.PI; + } + return result; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/listener/RedisNotificationMessageSubscriber.java b/src/main/java/com/techsor/datacenter/sender/listener/RedisNotificationMessageSubscriber.java new file mode 100644 index 0000000..0998f6d --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/listener/RedisNotificationMessageSubscriber.java @@ -0,0 +1,63 @@ +package com.techsor.datacenter.sender.listener; + + +import com.alibaba.fastjson2.JSON; +import com.techsor.datacenter.sender.components.GlobalSwitchStatusComponent; +import com.techsor.datacenter.sender.components.GuavaRedisCache; +import com.techsor.datacenter.sender.constants.MsgConstants; +import com.techsor.datacenter.sender.dto.RedisCommandReceiverDTO; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.Objects; + +import static com.techsor.datacenter.sender.constants.Constants.SWITCH_STATUS_PREFIX; + +@Slf4j +@Component +public class RedisNotificationMessageSubscriber implements MessageListener { + + + @Resource + private GlobalSwitchStatusComponent globalSwitchStatusComponent; + + @Resource + private GuavaRedisCache guavaRedisCache; + @Override + public void onMessage(Message message, byte[] pattern) { + String commandMessage = new String(message.getBody()); + if (StringUtils.isEmpty(commandMessage)) { + log.warn("Empty message received."); + return; + } + log.info("Message received: {}", commandMessage); + commandMessage = commandMessage.substring(1, commandMessage.length() - 1); + commandMessage= StringEscapeUtils.unescapeJson(commandMessage); + RedisCommandReceiverDTO redisCommandReceiverDTO = JSON.parseObject(commandMessage, RedisCommandReceiverDTO.class); + + if (redisCommandReceiverDTO == null) { + log.warn("Failed to parse message: {}", commandMessage); + return; + } + + if (StringUtils.equals(redisCommandReceiverDTO.getCommandType(), MsgConstants.REIDS_COMMAND_SWITCH_TYPE)) { + String value = redisCommandReceiverDTO.getValue(); + this.globalSwitchStatusComponent.updateSwitchStatus(Objects.equals(value, "on")); + } else { + if (StringUtils.equalsIgnoreCase(redisCommandReceiverDTO.getCommandType(), MsgConstants.REDIS_COMMAND_STAT_EVENT)) { + this.guavaRedisCache.syncToRedis(); + }else{ + + log.warn("Unknown command type: {}",redisCommandReceiverDTO.getCommandType()); + + } + } + + + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/listener/RedisStatsSwitchListener.java b/src/main/java/com/techsor/datacenter/sender/listener/RedisStatsSwitchListener.java new file mode 100644 index 0000000..1135d53 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/listener/RedisStatsSwitchListener.java @@ -0,0 +1,33 @@ +package com.techsor.datacenter.sender.listener; + +import com.techsor.datacenter.sender.components.GlobalSwitchStatusComponent; +import com.techsor.datacenter.sender.constants.Constants; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.Objects; + +@Component +public class RedisStatsSwitchListener implements ApplicationListener { + + @Resource + private RedisTemplate redisTemplate; + + @Resource + private GlobalSwitchStatusComponent globalSwitchStatusComponent; + @Override + public void onApplicationEvent(ApplicationStartedEvent event) { + if (!(event instanceof ApplicationStartedEvent)){ + return; + } + + Object switchStatus = redisTemplate.opsForValue().get(Constants.SWITCH_STATUS_PREFIX); + + if (Objects.nonNull(switchStatus)){ + this.globalSwitchStatusComponent.updateSwitchStatus(Boolean.parseBoolean(String.valueOf(switchStatus))); + } + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/processers/AsyncDataProcessor.java b/src/main/java/com/techsor/datacenter/sender/processers/AsyncDataProcessor.java new file mode 100644 index 0000000..553223a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/processers/AsyncDataProcessor.java @@ -0,0 +1,40 @@ +package com.techsor.datacenter.sender.processers; + +import com.techsor.datacenter.sender.service.IDataProcessService; +import com.techsor.datacenter.sender.service.KingIOServerService; +import com.techsor.datacenter.sender.service.St150Service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +@Service +public class AsyncDataProcessor { + private static Logger log= LoggerFactory.getLogger(AsyncDataProcessor.class); + + @Async + public void processKingIOServerAsync(String content, KingIOServerService kingIOServerService, IDataProcessService dataProcessService) { + try { + // 执行耗时任务 + String processJson = kingIOServerService.start(content); + dataProcessService.processKingIOServerData(processJson); + } catch (Exception e) { + // 记录异常日志 + log.error("Error while processing data asynchronously: " + e.getMessage()); + e.printStackTrace(); + } + } + + @Async + public void processST150Async(String content, St150Service st150Service, IDataProcessService dataProcessService) { + try { + // 执行耗时任务 + String processJson = st150Service.start(content); + dataProcessService.processSt150Data(processJson); + } catch (Exception e) { + // 记录异常日志 + log.error("Error while processing data asynchronously: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/processers/DeltaProcessor.java b/src/main/java/com/techsor/datacenter/sender/processers/DeltaProcessor.java new file mode 100644 index 0000000..c620cfd --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/processers/DeltaProcessor.java @@ -0,0 +1,191 @@ +package com.techsor.datacenter.sender.processers; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.techsor.datacenter.sender.components.DBMRestfulClient; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.entitiy.DeltaClientCacheEntity; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.JsonResponse; +import com.techsor.datacenter.sender.entitiy.delta.DeltaCacheEntity; +import com.techsor.datacenter.sender.entitiy.delta.DeltaNumEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.lang.reflect.Type; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * 用于处理 Delta 设备数据并将处理后的数据转发到 DBM 系统的服务。 + *

    + * 该服务将接收的 JSON 数据解码为 DeltaNumEntity 对象,根据设备类型确定每个设备的相关客户端 ID, + * 并构建要发送到 DBM 系统的消息。它支持多种 Delta 设备类型,处理数据格式化和传输。 + */ +@Service +public class DeltaProcessor { +// @Autowired + +// RedisUtils redisUtils; + @Resource + private CommonDAO commonDAO; + + @Resource + DBMRestfulClient DBMRestfulClient; + + private final static Logger myLogger = LoggerFactory.getLogger(DeltaProcessor.class); + + + /** + * 处理 Delta 设备数据的入口点。 + *

    + * 解析传入的 JSON 内容,按客户端 ID 对数据进行分类,并将处理后的数据消息发送到 DBM 系统。 + * + * @param content 包含 Delta 设备数据的 JSON 字符串。 + * @return JsonResponse 指示处理操作的结果。 + */ + public JsonResponse start(String content){ + //解析数据,数据格式为不带头的列表 + Type listType = new TypeToken>() {}.getType(); + List dataList = new Gson().fromJson(content, listType); + + //初始化clientId数组,因为一个clientId只能对应200个设备,这里一条数据可能存在属于多个clientId + List clientCacheList = new ArrayList<>(); + List deviceEntities; + + //初始化ClientId + if (dataList.size()>0){ + //初始化Delta类型列表,目前只有46,47,48,52 + ArrayList typeIdList = new ArrayList<>(); + typeIdList.add(45); + typeIdList.add(46); + typeIdList.add(47); + typeIdList.add(48); + typeIdList.add(52); + //获取所有属于Delta的设备信息 + deviceEntities = commonDAO.getDeviceInfoByTypeIds(typeIdList); + //初始化Delta绑定的所有clientId + List wsClientList = commonDAO.getWsClientIdListByTypeIds(typeIdList); + for (String item:wsClientList + ) { + DeltaClientCacheEntity cacheItem = new DeltaClientCacheEntity(); + cacheItem.setClientId(item); + ArrayList deviceItem = new ArrayList<>(); + cacheItem.setDeviceInfoList(deviceItem); + clientCacheList.add(cacheItem); + } + if (deviceEntities.size()<=0 || wsClientList.size()<=0){ + return JsonResponse.buildError(-1,"No matched device Id for delta"); + } + }else{ + return JsonResponse.buildError(-1,"No current Data parsed."); + } + //归类当前解析到的设备数据到clientCache列表中,完成后,clientCacheList中将clientId与设备列表对应关系 + for (DeltaNumEntity receivedItem:dataList){ + //遍历匹配收到的数据与数据库中读取的设备数据 + for (DeviceEntity deviceInfoItem:deviceEntities){ + if (deviceInfoItem.getDeviceId().equals(receivedItem.getId())){ + //匹配到设备数据后,将设备信息写入到对应的clientId缓存中 + for(DeltaClientCacheEntity cacheItem:clientCacheList){ + if (cacheItem.getClientId().equals(deviceInfoItem.getWsClientId())){ + DeltaCacheEntity deviceTemp = new DeltaCacheEntity(); + deviceTemp.setDeviceId(deviceInfoItem.getDeviceId()); + deviceTemp.setDeviceSN(deviceInfoItem.getDeviceSN()); + deviceTemp.setTypeId(deviceInfoItem.getTypeId()); + deviceTemp.setValue(receivedItem.getValue()); + cacheItem.getDeviceInfoList().add(deviceTemp); + } + } + } + } + } + +// //处理重复数据,未变化的数据不会被转发给DBM +// for(DeltaClientCacheEntity cacheItem:clientCacheList){ +// for (int i=0;i tempDeviceList = cacheItem.getDeviceInfoList(); + String finalMsg = "{"; + for(DeltaCacheEntity item:tempDeviceList){ + String message = generateJGCode(item,item.getTypeId()); + finalMsg = finalMsg+message; + } + finalMsg = finalMsg.substring(0,finalMsg.length()-1)+"}"; + //发送数据到DBM + try{ + if (!finalMsg.equals("}")){ + DBMRestfulClient.postJson(tempWsClientId,finalMsg); + } + }catch (Exception e){ + myLogger.error(e.getMessage()); + } + } + return JsonResponse.buildSuccess(200); + } + /** + * 根据 Delta 设备数据和设备类型 ID 生成 JSON 格式的字符串。 + *

    + * 根据 Delta 设备的类型构建带有特定键和值的 JSON 字符串,这些值按照 Delta 设备的类型进行格式化。 + * + * @param item 要格式化的 Delta 设备数据。 + * @param typeId Delta 设备的类型 ID,决定输出消息的格式。 + * @return 表示处理后的设备数据的 JSON 格式的字符串。 + */ + public String generateJGCode(DeltaCacheEntity item,Integer typeId){ + String JGCode="\""+item.getDeviceSN()+"_"; + String tempKey = ""; + switch (typeId){ + case 45: //Delta 测试 + tempKey="malfunctioned_test"; + JGCode = JGCode+tempKey+"\":"+item.getValue().toString()+","; + break; + case 46: //Delta 故障 + tempKey="delta_failure"; + if ((Boolean) item.getValue()==false){ + JGCode = JGCode+tempKey+"\": 0,"; + }else if ((Boolean) item.getValue()==true){ + JGCode = JGCode+tempKey+"\": 1,"; + } + break; + case 47: //Delta 計測 + tempKey="delta_measurement"; + JGCode = JGCode+tempKey+"\":"+item.getValue().toString()+","; + break; + case 48: //Delta 積算 + tempKey="delta_accrual"; + JGCode = JGCode+tempKey+"\":"+item.getValue().toString()+","; + break; + case 52: //Delta 積算 0.1倍率 + tempKey="delta_accrual"; + Double tempValue = Double.parseDouble(item.getValue().toString()) *0.1; + DecimalFormat df = new DecimalFormat("#0.00"); + JGCode = JGCode+tempKey+"\":"+df.format(tempValue)+","; + break; + } + + myLogger.debug("GenerateMessage: "+JGCode); + return JGCode; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/processers/DuplicateDataProcessor.java b/src/main/java/com/techsor/datacenter/sender/processers/DuplicateDataProcessor.java new file mode 100644 index 0000000..068d273 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/processers/DuplicateDataProcessor.java @@ -0,0 +1,83 @@ +package com.techsor.datacenter.sender.processers; + +import cn.hutool.crypto.digest.DigestAlgorithm; +import cn.hutool.crypto.digest.DigestUtil; +import com.techsor.datacenter.sender.utils.RedisUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.integration.redis.util.RedisLockRegistry; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * 数据去重处理器,用于从传入的数据中移除重复项。 + *

    + * 该类使用 Redis 作为存储介质,通过计算数据的 SHA1 哈希值作为唯一标识,利用分布式锁确保数据处理的原子性和一致性。 + */ +@Service +public class DuplicateDataProcessor { + + private final static Logger myLogger = LoggerFactory.getLogger(DuplicateDataProcessor.class); + + @Resource + private RedisLockRegistry redisLockRegistry; + + @Resource + private RedisUtils redisUtils; + /** + * 尝试从传入的数据中移除重复项。 + *

    + * 通过对内容计算 SHA1 哈希作为锁的键名,尝试获取一个分布式锁。如果获取锁成功且 Redis 中不存在该键名, + * 则认为数据不重复,将其写入 Redis 并返回 true;如果数据已存在,则返回 false,表示数据重复。 + * + * @param content 需要处理的数据内容。 + * @return 如果数据是首次出现且成功写入 Redis,则返回 true;如果数据重复或处理失败,则返回 false。 + */ + + public boolean removeDuplicateData(String content) { + //获取lockKeyName + String lockKeyName= DigestUtil.digester(DigestAlgorithm.SHA1).digestHex(content); + //此处排除并发下为空的情况 + if (StringUtils.isEmpty(lockKeyName)){ + return false; + } + //定义返回结果 + boolean result=false; + //获取分布式锁,进行去重 + Lock lock=this.redisLockRegistry.obtain(lockKeyName); + try{ + if (lock.tryLock(10, TimeUnit.SECONDS)){ + //有数据 + if (this.redisUtils.hasKey(lockKeyName)){ + + result=false; + }else{ + //没有数据,则写入redis + result=true; + this.redisUtils.add(lockKeyName,content,3600L,TimeUnit.SECONDS); + } + }else{ + return false; + } + }catch (InterruptedException e){ + // myLogger.error("an exception caught here :{}",e.getMessage(),e); + result=false; + }finally { + try{ + lock.unlock(); + }catch (Exception e){ + // myLogger.error("an exception caught here :{}",e.getMessage(),e); + } + } + + return result; + } + + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/processers/MetcomProcessor.java b/src/main/java/com/techsor/datacenter/sender/processers/MetcomProcessor.java new file mode 100644 index 0000000..2c870c0 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/processers/MetcomProcessor.java @@ -0,0 +1,139 @@ +package com.techsor.datacenter.sender.processers; + +import com.google.gson.Gson; +import com.techsor.datacenter.sender.components.DBMRestfulClient; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.dao.MetcomDAO; +import com.techsor.datacenter.sender.entitiy.JsonResponse; +import com.techsor.datacenter.sender.entitiy.metcom.MetcomEntity; +import com.techsor.datacenter.sender.entitiy.metcom.MetcomRecordEntity; +import com.techsor.datacenter.sender.entitiy.metcom.MetcomSpaceBleEntity; +import com.techsor.datacenter.sender.service.IDataProcessService; +import com.techsor.datacenter.sender.utils.RedisUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +/** + * Metcom数据处理器,用于处理来自Metcom设备的数据。 + *

    + * 此类接收JSON格式的Metcom数据,解析数据并根据业务逻辑处理,包括数据去重、发送进入或离开消息等。 + */ +@Service +public class MetcomProcessor { + private final static Logger myLogger = LoggerFactory.getLogger(MetcomProcessor.class); + @Autowired + RedisUtils redisUtils; + + @Resource + private IDataProcessService dataProcessService; + @Resource + private MetcomDAO metcomDAO; + + //数据处理入口,先判断该设备是否需要处理数据 + + /** + * 处理Metcom设备发送的数据的入口方法。 + *

    + * 此方法解析传入的JSON字符串为MetcomEntity对象,执行数据去重逻辑,并根据数据内容发送相应的进入或离开消息。 + * + * @param content Metcom设备发送的JSON格式数据。 + * @return JsonResponse 表示处理结果的响应对象。 + */ + public JsonResponse start(String content){ + //数据初步处理components + MetcomEntity metcomEntity = new MetcomEntity(); + metcomEntity = new Gson().fromJson(content,MetcomEntity.class); + + //匹配空间与BLE网关ID +// MetcomSpaceBleEntity spaceBleEntity = metcomDAO.getBleBySpace(metcomEntity.getSpaceId()); + MetcomSpaceBleEntity spaceBleEntity = new MetcomSpaceBleEntity(); + spaceBleEntity.setBleId(metcomEntity.getSpaceId()); + spaceBleEntity.setSpaceId(metcomEntity.getSpaceId()); + + if (null==spaceBleEntity){ + myLogger.error("No space ble data for "+metcomEntity.getSpaceId()); + return JsonResponse.buildSuccess("No space ble data for "+metcomEntity.getSpaceId()); + } + + //缓存数据,如果数据重复则不进行任何操作 + String tempSpaceIdWithBleId = redisUtils.getString(metcomEntity.getUuid(),""); + if (!tempSpaceIdWithBleId.equals(metcomEntity.getSpaceId())){ + //不重复则存储数据,并继续处理 + redisUtils.add(metcomEntity.getUuid(),metcomEntity.getSpaceId()); + }else{ + //数据重复则结束 + myLogger.debug("Duplicate Data, finish"); + return JsonResponse.buildSuccess("Duplicate Data, finish"); + } + + //计算是否需要发送离开数据,还是只发送进入数据 + MetcomRecordEntity recordEntity = metcomDAO.getRecordByUuid(metcomEntity); + //处理发送离开数据 + if (recordEntity!=null){ + myLogger.debug("Sending leave Msg ... "); + sendMsg(0, recordEntity.getBleId(), recordEntity.getUuid(), recordEntity.getSpaceId()); + } + //处理发送进入数据 + myLogger.debug("Sending enter Msg ... "); + sendMsg(1, spaceBleEntity.getBleId(), metcomEntity.getUuid(), metcomEntity.getSpaceId()); + + //记录此次进入历史 + metcomDAO.addRecord(metcomEntity.getUuid(), metcomEntity.getSpaceId(), spaceBleEntity.getBleId()); + + return JsonResponse.buildSuccess(200); + } + /** + * 根据状态、BLE网关ID和UUID发送消息。 + *

    + * 此方法构建进入或离开的消息并通过数据处理服务发送。 + * + * @param status 表示设备状态的整数(例如,0代表离开,1代表进入)。 + * @param bleId BLE网关的ID。 + * @param uuid 设备的UUID。 + * @param spaceId 空间ID,用于确定消息发送目标。 + */ + private void sendMsg(Integer status,String bleId,String uuid,String spaceId){ + //生成发送的内容 + String message = generateJGCode(status,bleId,uuid); + + //获取sessionId + + String clientId = "rydw0005"; //clientId写死 +// List tempDeviceList = commonDAO.getWSClientByDeviceId(spaceId); +// if (tempDeviceList.size()>0){ +// clientId = tempDeviceList.get(0).getWsClientId(); +// }else{ +// JsonResponse.buildError(-1,"No matched WS client"); +// } + + if (StringUtils.isEmpty(clientId)){ + JsonResponse.buildError(-1,"No matched WS client"); + }else { + this.dataProcessService.saveMetcom(message,uuid); + } + } + + /** + * 根据状态、BLE网关ID和UUID生成JSON格式的消息字符串。 + *

    + * 构建包含当前状态、网关MAC地址和设备MAC地址的JSON消息。 + * + * @param status 表示设备状态的整数(0代表离开,1代表进入)。 + * @param bleId BLE网关的ID。 + * @param uuid 设备的UUID。 + * @return 构建的JSON格式消息字符串。 + */ + private String generateJGCode(Integer status,String bleId,String uuid){ + String msg = "{\"currentStatus\":\"?1\",\"gatewayMacAddr\":\"?2\",\"deviceMacAddr\":\"?3\",\"deviceMacAddr_f\":\"?4\"}"; + msg = msg.replace("?1",status.toString()); + msg = msg.replace("?2",bleId); + msg = msg.replace("?3",uuid); + msg = msg.replace("?4",uuid); + return msg; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/processers/NBIProcessor.java b/src/main/java/com/techsor/datacenter/sender/processers/NBIProcessor.java new file mode 100644 index 0000000..6ed2685 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/processers/NBIProcessor.java @@ -0,0 +1,137 @@ +package com.techsor.datacenter.sender.processers; + +import com.google.gson.Gson; +import com.techsor.datacenter.sender.dao.NbiDAO; +import com.techsor.datacenter.sender.components.DBMRestfulClient; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.JsonResponse; +import com.techsor.datacenter.sender.entitiy.nbi.NbiData; +import com.techsor.datacenter.sender.entitiy.nbi.NbiEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; + + +/** + * NBI数据处理器,用于处理来自NBI设备的数据。 + *

    + * 此类负责解析传入的JSON格式的NBI数据,将数据映射到相应的设备,并根据设备数据生成并发送JSON消息。 + */ +@Service +public class NBIProcessor { + private final static Logger myLogger = LoggerFactory.getLogger(NBIProcessor.class); + + @Resource + private NbiDAO nbiDAO; + + @Resource + private CommonDAO commonDAO; + @Resource + DBMRestfulClient DBMRestfulClient; + //数据处理入口,先判断该设备是否需要处理数据 + /** + * 处理NBI设备发送的数据的入口方法。 + *

    + * 此方法解析传入的JSON字符串为NbiEntity对象,并根据解析的数据内容生成消息发送给DBM系统或相应的客户端。 + * + * @param content NBI设备发送的JSON格式数据。 + * @return JsonResponse 表示处理结果的响应对象。 + */ + public JsonResponse start(String content){ + //数据初步处理components + NbiEntity nbiEntity = new NbiEntity(); + nbiEntity = new Gson().fromJson(content,NbiEntity.class); + + + List deviceEntities = nbiDAO.getDeviceIdBySN(nbiEntity.getPayload().getDevice()); + String deviceId =""; + if (deviceEntities.size()>0){ + deviceId = deviceEntities.get(0).getDeviceId(); + }else{ + JsonResponse.buildError(-1,"No matched device Id for :"+nbiEntity.getPayload().getDevice()); + } + + if (deviceId == null || deviceId.equals("")){ + return JsonResponse.buildError(-1,"No matched deviceId"); + } + //生成发送的内容 + String message = generateJGCode(nbiEntity,deviceId); + + //获取sessionId + + String clientId = ""; + List tempDeviceList = commonDAO.getWSClientByDeviceId(deviceId); + if (tempDeviceList.size()>0){ + clientId = tempDeviceList.get(0).getWsClientId(); + }else{ + JsonResponse.buildError(-1,"No matched WS client"); + } + + if (clientId.equals("")){ + JsonResponse.buildError(-1,"No matched WS client"); + }else { + DBMRestfulClient.postJson(clientId,message); + } + + return JsonResponse.buildSuccess(200); + } + /** + * 根据NBI实体对象和设备ID生成JSON格式的消息字符串。 + *

    + * 构建包含设备数据的JSON消息,该消息基于NBI设备的不同数据类型和设备ID进行格式化。 + * + * @param nbiEntity 解析后的NBI实体对象。 + * @param deviceId 与NBI数据相关联的设备ID。 + * @return 构建的JSON格式消息字符串。 + */ + private String generateJGCode(NbiEntity nbiEntity,String deviceId){ + String JGCode = "{"; + for (int i=0;i + * 此类接收 JSON 格式的 Nittan 数据,验证数据签名和设施信息,解析数据并生成相应的 DBM 代码消息, + * 然后将消息发送给适当的 Websocket 客户端或 DBM 系统。 + */ +@Service +public class NittanProcessor { + private static final Logger logger = LoggerFactory.getLogger(NittanProcessor.class); + @Resource + DBMRestfulClient DBMRestfulClient; + + @Resource + private NittanCompanyDAO nittanCompanyDAO; + + @Resource + private CommonDAO commonDAO; + + @Resource + private NittanDAO nittanDAO; + /** + * 接收并处理 Nittan 数据的入口方法。 + *

    + * 验证数据签名,检查设施信息的正确性,生成并发送 DBM 代码消息。 + * + * @param content Nittan 设备发送的 JSON 格式数据。 + * @return JsonResponse 表示处理结果的响应对象。 + * @throws Exception 处理过程中可能抛出的异常。 + */ + public JsonResponse start(String content) throws Exception { + String deviceSN="",companyCode="",signature=""; + Integer timeStamp; + + //Print the data received + NittanEntity nittanEntity = new Gson().fromJson(content,NittanEntity.class); + logger.debug("-------------Received Data-------------"); + logger.debug("{}", JSON.toJSON(nittanEntity)); + + deviceSN = nittanEntity.getNotice_info().get(0).getLocation_info().getLocation_address(); + companyCode = nittanEntity.getAuth().getCode(); + signature = nittanEntity.getAuth().getSignature(); + timeStamp = nittanEntity.getAuth().getTimestamp(); + + List> testList = this.nittanCompanyDAO.getCompanyByCompanyCode(companyCode); + String apikey = (String) testList.get(0).get("apikey"); + String dbCompanyCode = nittanEntity.getAuth().getSubscription_id(); + + String waitEncryptJson="http://fire.techsor.co.jp:8200/api/v1/to_dbm/nittan"+"?subscription_id="+dbCompanyCode+"×tamp="+timeStamp; + logger.debug("String for signature : {}",waitEncryptJson); + String generatedSignature = Sha1Utils.getSignature(waitEncryptJson,apikey).toString(); + + logger.debug("Received Signature: {}",signature); + logger.debug("Correct Signature: {}",generatedSignature); + + //Judge the signature. Check if this send is from the right provider. + if(!signature.equals(generatedSignature) && !signature.equals("techsor")){ + logger.warn("Signature Not correct."); + return JsonResponse.buildError("Signature Not correct."); + } + + //判断Facility是否正确 + logger.debug("Received Facility id : {}",nittanEntity.getFacility_info().getFacility_id()); + logger.debug("Received Facility maker : {}",nittanEntity.getFacility_info().getFacility_maker()); + if(!nittanEntity.getFacility_info().getFacility_maker().equals("NITTAN")){ + logger.warn("Facility Maker Not correct."); + return JsonResponse.buildError("Facility Maker Not correct."); + } + if (!nittanEntity.getFacility_info().getFacility_id().equals("Nihonbashi-Building") && !nittanEntity.getFacility_info().getFacility_id().equals("INNOTECH")){ + logger.warn("Facility Id Not correct."); + return JsonResponse.buildError("Facility Id Not correct."); + } + + + //计算需要转发的Code内容 + String message = ""; + + message = this.generateDBMCode(deviceSN,nittanEntity); + + //获取转发时使用的WS客户端 + String wsClientID = whichClient(deviceSN); + if (StringUtils.isEmpty(wsClientID)){ + return JsonResponse.buildError(1,"No matched wsClient"); + } + if (StringUtils.isEmpty(message)){ + return JsonResponse.buildError(1,"No matched Code"); + } + + DBMResponse response = DBMRestfulClient.postJson(wsClientID,message); + logger.debug("Post success"); +// if (!response.getFlag()){ +// logger.error("Post To DBM Error! ClientID:"+wsClientID+" MESSAGE:"+message); +// } + return JsonResponse.buildSuccess(200); + } + /** + * 根据设备 SN 确定信息应该发送到哪个 Websocket 客户端。 + *

    + * 使用设备 SN 从数据库中查找对应的 Websocket 客户端 ID。 + * + * @param deviceSN 设备的序列号。 + * @return 目标 Websocket 客户端的 ID。 + */ + //判断该将信息发往那一个WSclient + private String whichClient(String deviceSN){ + String wsClientID = null; + List deviceList = commonDAO.getWSClientByDeviceId(deviceSN); + if (deviceList.size()>0){ + wsClientID = deviceList.get(0).getWsClientId(); + } + return wsClientID; + } + + /** + * 根据设备SN和NittanEntity对象生成DBM格式的代码。 + *

    + * 根据设备信息和通知信息,查询数据库获取对应的DBM代码,并构建消息字符串。 + * + * @param deviceSN 设备的序列号。 + * @param nittanEntity 解析后的Nittan实体对象。 + * @return 生成的DBM格式代码字符串,如果没有匹配的代码数据,则返回空字符串。 + */ + //获取数据对应的DBMCode + private String generateDBMCode(String deviceSN,NittanEntity nittanEntity){ + + List> codeList = nittanDAO.getDBMCode(nittanEntity.getNotice_info().get(0).getDevice_info().getDevice_class(),nittanEntity.getNotice_info().get(0).getNotice_type(), + nittanEntity.getNotice_info().get(0).getNotice_level(),nittanEntity.getNotice_info().get(0).getNotice_status(),nittanEntity.getFacility_info().getFacility_mode()); + + if(codeList.size()>=1){ + String dbmCode = (String)codeList.get(0).get("dbm_code"); + logger.debug(dbmCode); +// return "{\""+deviceSN+"_TEST\":"+dbmCode+"}"; + return "{\""+deviceSN+"\":"+dbmCode+"}"; + }else{ + logger.error("No Matched Code Data"); + return ""; + } + + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/processers/OCRProcessor.java b/src/main/java/com/techsor/datacenter/sender/processers/OCRProcessor.java new file mode 100644 index 0000000..86bdc27 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/processers/OCRProcessor.java @@ -0,0 +1,127 @@ +package com.techsor.datacenter.sender.processers; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.techsor.datacenter.sender.components.DBMRestfulClient; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.dao.OcrDao; +import com.techsor.datacenter.sender.entitiy.DBMResponse; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.JsonResponse; +import com.techsor.datacenter.sender.entitiy.nittan.OCREntity; + +import java.lang.reflect.Type; +import java.util.List; +import com.techsor.datacenter.sender.constants.DeviceTypeConstants; +import com.techsor.datacenter.sender.entitiy.OcrHistoryEntity; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; + +/** + * OCR数据处理器,用于处理来自OCR设备的数据。 + *

    + * 此类负责解析OCR设备发送的JSON格式数据,并根据解析的数据内容进行处理,包括识别设备类型、计算差异值、保存历史记录等。 + */ +@Service +public class OCRProcessor { + private String deviceSN = ""; + private String value = ""; + private String message = ""; + + @Resource + private OcrDao ocrDao; + @Resource + DBMRestfulClient DBMRestfulClient; + @Resource + private CommonDAO commonDAO; + + /** + * OCR设备数据处理的入口方法。 + *

    + * 解析传入的JSON字符串为OCR实体列表,根据设备类型和消息类型处理数据,并发送处理结果。 + * + * @param content OCR设备发送的JSON格式数据。 + * @return JsonResponse 表示处理结果的响应对象。 + */ + //数据处理入口 + public JsonResponse start(String content){ + Type jsonType = new TypeToken>() {}.getType(); + List ocrEntities = new Gson().fromJson(content,jsonType); + for (int i=0;i + * 从数据库查询与设备序列号关联的设备信息。 + * + * @param deviceSN 设备的序列号。 + * @return 匹配到的设备实体,如果没有找到匹配项,则返回null。 + */ + //判断该将信息发往那一个WSclient + private DeviceEntity whichClient(String deviceSN){ + DeviceEntity deviceEntity = null; + + List deviceList = commonDAO.getDeviceInfoBySN(deviceSN); + if (deviceList.size()>0){ + deviceEntity = deviceList.get(0); + } + return deviceEntity; + } + /** + * 获取指定设备序列号的前一条OCR数据记录。 + *

    + * 查询数据库获取设备的最新OCR历史记录。 + * + * @param deviceSN 设备的序列号。 + * @return 最新的OCR历史记录实体,如果没有找到匹配项,则返回null。 + */ + private OcrHistoryEntity getPrevData(String deviceSN){ + OcrHistoryEntity ocrHistoryEntity = null; + List deviceList = ocrDao.getOcrInfoBySN(deviceSN); + if (deviceList.size()>0){ + ocrHistoryEntity = deviceList.get(0); + } + return ocrHistoryEntity; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/processers/OviphoneHelper.java b/src/main/java/com/techsor/datacenter/sender/processers/OviphoneHelper.java new file mode 100644 index 0000000..4da9b9f --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/processers/OviphoneHelper.java @@ -0,0 +1,73 @@ +package com.techsor.datacenter.sender.processers; + +import com.techsor.datacenter.sender.components.DBMRestfulClient; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.dao.OviphoneDAO; +import com.techsor.datacenter.sender.entitiy.BleHistoryEntity; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +/** + * Oviphone 发送助理,循环检测数据更新并发送 + */ +@Service +public class OviphoneHelper { + private static final Logger logger = LoggerFactory.getLogger(OviphoneHelper.class); + @Resource + DBMRestfulClient DBMRestfulClient; + @Resource + private CommonDAO commonDAO; + + @Resource + private OviphoneDAO oviphoneDAO; + /** + * 检测数据库中更新的进出数据记录,5s一条的间隔发送 + */ + public void send(){ + //开始处理进入数据 + + List bleHistoryList = oviphoneDAO.getSendingList(); + //获取到待发送的列表后,间隔发送 + for (int i=0;i tempDeviceList = commonDAO.getWSClientByDeviceId(item.getBleBluetoothMac()); + logger.debug("Sending Oviphone IMEI="+item.getBleBluetoothMac()); + if (tempDeviceList.size()>0){ + clientId = tempDeviceList.get(0).getWsClientId(); + logger.debug("Oviphone ClientID="+clientId); + }else{ + logger.error("No matched WS client"); + } + String beaconID = StringUtils.lowerCase(item.getBleBluetoothMac(),Locale.ROOT); + + String message="{ \"currentStatus\":\""+bleHistoryList.get(i).getStatus()+"\", \"gatewayMacAddr\":\""+beaconID+"\", \"deviceMacAddr\":\""+item.getBleBluetoothMac()+"\", \"deviceMacAddr_f\":\""+item.getBleBluetoothMac()+"\" }"; + + logger.debug("Start sending Oviphone message:"+message); + DBMRestfulClient.postJson(clientId,message); + + oviphoneDAO.deleteById(item.getId()); + + logger.debug("Send Oviphone Data Sccess"); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/processers/OviphoneProcessor.java b/src/main/java/com/techsor/datacenter/sender/processers/OviphoneProcessor.java new file mode 100644 index 0000000..2c5f47f --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/processers/OviphoneProcessor.java @@ -0,0 +1,165 @@ +package com.techsor.datacenter.sender.processers; + +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.dao.OviphoneDAO; +import com.techsor.datacenter.sender.entitiy.BleHistoryEntity; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.JsonResponse; +import com.techsor.datacenter.sender.utils.TimeUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.text.ParseException; +import java.util.List; + +@Service +public class OviphoneProcessor { + private static final Logger logger = LoggerFactory.getLogger(OviphoneProcessor.class); + + @Resource + private CommonDAO commonDAO; + + @Resource + private OviphoneDAO oviphoneDAO; + //数据处理入口,先判断该设备是否需要处理数据 + public JsonResponse start(String content) throws ParseException, InterruptedException { + //数据初步处理,判断蓝牙是进入还是出去 + + String WN = getValueByKey(content,"wn"); + String IMEI = getValueByKey(content,"IMEI"); + String beaconID = getValueByKey(content,"BTInfo"); + String type = getValueByKey(content,"type"); + String utcTime = getValueByKey(content,"BTUtcTime"); + utcTime = utcTime.replace("%2F","-"); + utcTime = utcTime.replace("%3A",":"); + utcTime = utcTime.replace("+"," "); + String ts = TimeUtils.strToUTCUnixTime(utcTime)/1000+""; + + //获取设备对应的SESSIONID + + String clientId = ""; + List tempDeviceList = commonDAO.getWSClientByDeviceId(IMEI); + if (tempDeviceList.size()>0){ + clientId = tempDeviceList.get(0).getWsClientId(); + logger.debug("Oviphone ClientID="+clientId); + }else{ + JsonResponse.buildError(-1,"No matched WS client"); + } + + //wn=12或者type=38意味着离开上一个蓝牙设备 + if (WN.equals("16") || type.equals("38")){ + processBLEOut(IMEI,beaconID,clientId,ts); + }else{ + processBLEIn(IMEI,beaconID,clientId,ts); + } + + + + return JsonResponse.buildSuccess(""); + } + + /** + * 处理蓝牙进入 + * @param IMEI + * @param beaconID + */ + private JsonResponse processBLEIn(String IMEI,String beaconID,String clientId,String ts) throws InterruptedException { + logger.debug("process in data"); + Thread.sleep(2000); //休眠2s,避免离开数据未写入就写入进入数据 + + JsonResponse jsonResponse = new JsonResponse(); + if(IMEI.equals("") || beaconID.equals("")){ + logger.debug("No IMEI or Beacon information"); + return JsonResponse.buildError(-1,"No IMEI or Beacon information"); + } + + String[] beaconParams = beaconID.split("%40"); + beaconID = beaconParams[0]+beaconParams[1]; + + logger.debug("Oviphone IMEI="+IMEI); + logger.debug("Oviphone beaconID="+beaconID); + + //开始处理进入数据 + + + //如果最新一条数据是进入数据 + List bleHistoryEntities = oviphoneDAO.getLatestByIMEI(IMEI,ts); + if (bleHistoryEntities.size()>0){ + //有数据的情况下,查看最新数据是不是进入 + BleHistoryEntity latestBleHistory = bleHistoryEntities.get(0); + //如果最新数据是进入,则添加一条退出数据 + if (latestBleHistory.getStatus()==1){ + logger.warn("Latest status is in, Generating one [out] data"); + oviphoneDAO.addRecord(beaconID,IMEI,0,ts); + logger.warn("Generating one [out] data success"); + } + //添加进入记录 + if(oviphoneDAO.addRecord(beaconID,IMEI,1,ts)){ + logger.debug("Added one [in] data"); + }else{ + jsonResponse = JsonResponse.buildError("Add ble record failed, please retry."); + return jsonResponse; + } + }else{ + //之前没有数据,添加进入记录 + if(oviphoneDAO.addRecord(beaconID,IMEI,1,ts)){ + logger.debug("Added one [in] data"); + }else{ + jsonResponse = JsonResponse.buildError("Add ble record failed, please retry."); + return jsonResponse; + } + + } + + jsonResponse = JsonResponse.buildSuccess(""); + return jsonResponse; + } + + /** + * 处理蓝牙离开 + * @param IMEI + */ + private JsonResponse processBLEOut(String IMEI,String beaconID,String clientId,String ts) throws InterruptedException { + JsonResponse jsonResponse = new JsonResponse(); + + List bleHistoryEntities = oviphoneDAO.getLatestInByIMEI(IMEI,ts); + if (bleHistoryEntities.size()<0){ + jsonResponse = JsonResponse.buildError("No old bluetooth in Data"); + }else{ + //有数据的情况下,发送离开的状态指示 + BleHistoryEntity latestBleHistory = bleHistoryEntities.get(0); +// //如果最新数据是退出,则不操作 +// if (latestBleHistory.getStatus()==0){ +// logger.debug("Latest status is out, do nothing."); +// jsonResponse = JsonResponse.buildError("Latest status is out, do nothing."); +// }else{ + if(oviphoneDAO.addRecord(latestBleHistory.getBleGatewayMac(),IMEI,0,ts)){ + //添加离开的记录,但不发送,交给发送助理统一处理 +// String message="{ \"currentStatus\":\"0\", \"gatewayMacAddr\":\""+latestBleHistory.getBleGatewayMac().toLowerCase(Locale.ROOT)+"\", \"deviceMacAddr\":\""+IMEI+"\", \"deviceMacAddr_f\":\""+IMEI+"\" }"; + }else{ + jsonResponse = JsonResponse.buildError("Add ble record failed, please retry."); + } +// } + } + if (!StringUtils.isEmpty(beaconID)){ + Thread.sleep(5000); + processBLEIn(IMEI,beaconID,clientId,ts); + } +// jsonResponse =JsonResponse.buildSuccess("Process bluetooth leave success"); + return jsonResponse; + } + + public String getValueByKey(String content,String key){ + if (content.contains(key)){ + content = content.substring(content.indexOf(key)+key.length()+1); + content = content.split("&")[0]; + }else{ + return ""; + } + return content; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/processers/ZETAProcessor.java b/src/main/java/com/techsor/datacenter/sender/processers/ZETAProcessor.java new file mode 100644 index 0000000..601650e --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/processers/ZETAProcessor.java @@ -0,0 +1,647 @@ +package com.techsor.datacenter.sender.processers; + +import com.google.gson.Gson; +import com.techsor.datacenter.sender.dao.ZETADeviceDAO; +import com.techsor.datacenter.sender.components.DBMRestfulClient; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.dao.SkyDAO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.SkyEnvEntity; +import com.techsor.datacenter.sender.entitiy.ZETABaseEntity; +import com.techsor.datacenter.sender.entitiy.bluetooth.BlueToothDeviceEntity; +import com.techsor.datacenter.sender.helpers.AngleHelper; +import com.techsor.datacenter.sender.utils.HexUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * ZETA设备数据处理器,用于处理从ZETA设备接收的数据。 + *

    + * 此类负责解析ZETA设备发送的JSON格式数据,并根据解析的数据内容进行处理,包括设备数据的分析、消息的生成和发送等。 + */ +@Service +public class ZETAProcessor { + private static final Logger logger = LoggerFactory.getLogger(ZETAProcessor.class); + @Resource + DBMRestfulClient DBMRestfulClient; + + @Resource + ZETADeviceDAO deviceDAO; + + @Resource + private CommonDAO commonDAO; + + @Resource + private SkyDAO skyDAO; + + /** + * 处理接收到的ZETA设备数据。 + *

    + * 解析传入的JSON字符串为ZETA基础实体,并对每个匹配的设备执行数据分析和消息发送操作。 + * + * @param content 从ZETA设备接收到的JSON格式数据。 + */ + //数据处理入口,先判断该设备是否需要处理数据 + public void start(String content){ + //数据初步处理components + Gson gson = new Gson(); + ZETABaseEntity zetaBaseEntity = gson.fromJson(content,ZETABaseEntity.class); + + List deviceList = deviceDAO.queryDeviceByDeviceID(zetaBaseEntity.getMsgParam().getMsUid()); + for (DeviceEntity device:deviceList) { + logger.debug("Device : {}, Device Type : {}, Device Data: {}",new Object[]{device.getDeviceId(), device.getTypeId(),zetaBaseEntity.getMsgParam().getData()}); + + //计算发送的内容和客户端SessionID + List messageList = analysis(device.getDeviceId(),device.getTypeId(),zetaBaseEntity.getMsgParam().getData()); + if (messageList==null){ + logger.error("Data is null."); + return; + } + String seesionID = ""; + List dataList = this.commonDAO.getWSClientByDeviceId(device.getDeviceId()); + if (dataList.size()>0){ + seesionID = dataList.get(0).getWsClientId(); + } + + //发送数据,支持多条数据轮询发送 + for (int i=0;i + * 分析设备数据,生成可发送的消息列表,每个消息对应一个特定的设备状态或测量值。 + * + * @param deviceId 设备的唯一标识符。 + * @param deviceType 设备的类型代码。 + * @param value 从设备接收到的数据值。 + * @return 生成的消息列表,如果数据无法解析或不需要处理,则返回null或空列表。 + */ + private List analysis(String deviceId,Integer deviceType,String value){ + logger.debug("analysisedType: {}",deviceType); + logger.debug("value to analysis: {}", value); + + if (Objects.isNull(value)){ + return null; + } + + if(value.startsWith("e0")){ + value.substring(2); + } + + String analysisValue = ""; + ArrayList messageList = new ArrayList<>(); + switch (deviceType){ + case 1: + //4-20设备数据解析 + if(value.startsWith("01")){ + analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0))+""; + logger.debug("analysisedValue: "+ analysisValue); + String message = "{\""+deviceId+"_ultrasonic_flow_420\":"+analysisValue+"}"; + messageList.add(message); + } + break; + case 2: + //纵行水位传感器解析 + if(value.startsWith("02") || value.startsWith("03")){ + analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(4),16)/100.0))+""; + String message = "{\""+deviceId+"_water_level\":"+analysisValue+"}"; + messageList.add(message); + }else if (value.startsWith("04")){ + analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(2,6),16)/100.0))+""; + String message = "{\""+deviceId+"_water_level\":"+analysisValue+"}"; + messageList.add(message); + } + logger.debug("analysisedValue: "+ analysisValue); + break; + case 3: + //纵行干接点传感器解析 + if(value.startsWith("01")){ + String message=""; + if (value.equals("0101")){ + message = "{\""+deviceId+"_dry_contact\":1}"; + analysisValue="1"; + }else if (value.equals("0100")){ + message = "{\""+deviceId+"_dry_contact\":0.0}"; + analysisValue="0.0"; + } + if (!message.equals("")){ + messageList.add(message); + } + }else{ + String message=""; + if (value.equals("0201")){ + message = "{\""+deviceId+"_dry_contact\":1}"; + analysisValue="1"; + }else if (value.equals("0202")){ + message = "{\""+deviceId+"_dry_contact\":0.0}"; + analysisValue="0.0"; + } + if (!message.equals("")){ + messageList.add(message); + } + } + + logger.debug("analysisedValue: "+ analysisValue); + break; + case 4: + //ZETA-BLEGW-0903 + if(value.startsWith("BB")){ + analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(10),16)/100.0))+""; + String message = "{\""+deviceId+"_bth_gateway\":"+analysisValue+"}"; + messageList.add(message); + } + logger.debug("analysisedValue: "+ analysisValue); + break; + case 5: + //ZETA多路干接点 + if(value.startsWith("01")){ + analysisValue = value.substring(2); + String binaryStr = HexUtils.parseHexStr2Binary(analysisValue,16); + for (int j=binaryStr.length()-1;j>=0;j--){ + if (binaryStr.charAt(j)=='0'){ + messageList.add("{\""+deviceId+"_driving_condition_ch"+(16-j)+"\":0}"); + }else if (binaryStr.charAt(j)=='1'){ + messageList.add("{\""+deviceId+"_driving_condition_ch"+(16-j)+"\":1}"); + } + } + +// String message = "{\""+deviceId+"_dry_contact\":"+analysisValue+"}"; +// messageList.add(message); + } + logger.debug("analysisedValue: "+ analysisValue); + break; +// case 6: +// //ZETA标准485 +// if(value.startsWith("01")){ +// analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0))+""; +// String message = "{\""+deviceId+"_485\":"+analysisValue+"}"; +// messageList.add(message); +// } +// logger.debug("analysisedValue: "+ analysisValue); +// break; + case 7: + //4-20水压设备数据解析 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(100-0); + logger.debug("calculated Value: "+ calValue); + analysisValue = calValue+""; + String message = "{\""+deviceId+"_ultrasonic_flow_420\":"+analysisValue+"}"; + messageList.add(message); + } + break; + case 9: + //蓝牙GateWay + String data = value; + List beacons = new ArrayList<>(); + while(data.length()>=10){ + BlueToothDeviceEntity blueToothDeviceEntity = new BlueToothDeviceEntity(); + blueToothDeviceEntity.setDeviceId(Integer.valueOf(data.substring(0,4),16)+""+Integer.valueOf(data.substring(4,8),16)); + blueToothDeviceEntity.setFlag(data.substring(9,10)); + logger.debug("BeaconID:"+blueToothDeviceEntity.getDeviceId()); + logger.debug("flag:"+blueToothDeviceEntity.getFlag()); + data = data.substring(10); + + beacons.add(blueToothDeviceEntity); + } + for (int i=0;i skyEnvEntities = skyDAO.getLatestRainFallRecord(deviceId); + if (skyEnvEntities.size()<1){ + logger.debug("There no last rainFall data of "+deviceId); + }else{ + logger.debug("ranFallValue = "+ranfallValue+" - "+skyEnvEntities.get(0).getRainFall()); + ranfallValue = ranfallValue-skyEnvEntities.get(0).getRainFall(); + } + + String message = "{\"" + +deviceId+"_wind_direction\":"+windDirectionValue+",\"" + +deviceId+"_wind_speed\":"+windSpeedValue+",\"" + +deviceId+"_Instantaneous_wind_speed\":"+instantaneousWindSpeedValue+",\"" + +deviceId+"_temperature\":"+temperatureValue+",\"" + +deviceId+"_humidity\":"+humidityValue+",\"" + +deviceId+"_illumination\":"+illuminationValue+",\"" + +deviceId+"_rainfall\":"+ranfallValue+",\"" + +deviceId+"_uv_quantity\":"+uvQuantityValue + +"}"; + messageList.add(message); + + //转发完后,记录下当前雨量数据 + skyDAO.addRainFallRecord(deviceId,ranfallTemp+""); + } + break; + case 21: + //水浸传感器 + case 22: + //水浸传感器(绳子) + String leakValue = ""; + if(value.startsWith("01")){ + if (value.endsWith("01")){ + leakValue = "1"; + }else if (value.endsWith("02")){ + leakValue = "0"; + } + + }else if(value.startsWith("02")){ + leakValue = "1"; + }else if(value.startsWith("03")){ + leakValue = "0"; + } + //判断成功解析后发送 + if (!leakValue.equals("")){ + String message = "{\""+deviceId+"_leakage\":"+leakValue+"}"; + messageList.add(message); + } + + break; + case 24: + //420 电流版本:0-5A量程 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(5-0); + logger.debug("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_current_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + }else if(value.startsWith("02") || value.startsWith("03")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(4),16)/100.0)); + logger.info("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(5-0); + logger.info("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_current_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 25: + //420测试用 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(5-0); + logger.debug("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_test_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 26: + //485测试用 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + analysisValue = anaValue+""; + String message = "{\""+deviceId+"_test_485\":"+analysisValue+"}"; + messageList.add(message); + } + break; + case 28: + //420压力2.5Mpa + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = (anaValue-4)*0.1625 - 0.1; + logger.debug("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_pressure_M025_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + }else if(value.startsWith("02") || value.startsWith("03")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(4,8),16)/100.0)); + logger.info("analysisedValue: "+ value); + double calValue = (anaValue-4)*0.1625 - 0.1; + logger.info("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_pressure_M025_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 29: + //420压力1Mpa + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = (anaValue-4)*0.06875 - 0.1; + logger.info("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_pressure_M010_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 30: + //ZETA電流传感器-东建 + if(value.startsWith("01")){ + String temperatureValue = Float.valueOf((float) (Integer.valueOf(value.substring(4,8),16)/10.0))+""; + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-currentValue\":"+temperatureValue+"}"; + messageList.add(message); + } + break; + case 31: + //420压力1Mpa-采集值-东建 + case 32: + //420压力1Mpa-东建 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.info("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(10-0); + logger.info("calculated Value: "+ calValue); + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-current\":"+anaValue+",\""+deviceId+"-keyence\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } else if(value.startsWith("02") || value.startsWith("03")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(4,8),16)/100.0)); + logger.info("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(10-0); + logger.info("calculated Value: "+ calValue); + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-current\":"+anaValue+",\""+deviceId+"-keyence\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 33: + //水浸传感器-东建 + String leakValue2 = ""; + if(value.startsWith("01")){ + if (value.endsWith("01")){ + leakValue2 = "1"; + }else if (value.endsWith("02")){ + leakValue2 = "0"; + }else if (value.equals("0100")){ + leakValue2 = "0"; + } + }else if(value.startsWith("02")){ + leakValue2 = "1"; + }else if(value.startsWith("03")){ + leakValue2 = "0"; + } + //判断成功解析后发送 + if (!leakValue2.equals("")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-waterIntrusionStatus\":"+leakValue2+"}"; + messageList.add(message); + } + break; + case 34: + // 门磁传感器 -门状态-东建 + case 35: + //门磁传感器 -开门次数-东建 + if(value.startsWith("0a")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-doorStatus\":"+1+"}"; + messageList.add(message); + }else if(value.startsWith("0b")) { + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-doorStatus\":"+0+"}"; + messageList.add(message); + } else if(value.startsWith("01")){ + Integer times = Integer.valueOf(value.substring(2),16); + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-openTimes\":"+times+"}"; + messageList.add(message); + } + break; + case 36: + //人体感应-东建 + if( value.equals("01")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-checkResult\":"+1+"}"; + messageList.add(message); + } + if( value.equals("00")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-checkResult\":"+0+"}"; + messageList.add(message); + } + break; + case 41: + //温湿度-东建 + if(value.startsWith("01")){ + String temperatureValue = Float.valueOf((float) (Integer.valueOf(value.substring(2,6),16)/10.0))+""; + String humidity = Float.valueOf(Integer.valueOf(value.substring(6,8),16))+""; + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-temperature\":"+temperatureValue+",\""+deviceId+"-humidity\":"+humidity+"}"; + messageList.add(message); + } + break; + case 42: + //JAZE CO2 + if (value.length()==12){ + Float co2Value = Float.valueOf((float) (Integer.valueOf(value.substring(8,12),16)/1)); + Float temperatureValue = Float.valueOf((float) (Integer.valueOf(value.substring(2,6),16)/10.0)); + Float humidityValue = Float.valueOf((float) (Integer.valueOf(value.substring(6,8),16)/1)); + + String message = "{\""+deviceId+"_co2\":"+co2Value+",\""+deviceId+"_temperature\":"+temperatureValue+",\""+deviceId+ + "_humidity\":"+humidityValue+"}"; + messageList.add(message); + }else { + logger.error("SZC100 CO2 data length wrong, correct 26, received "+value.length()); + } + + break; + case 49: + // BLE漏水 + for (int i=0;i= 30){ + String power = value.substring(2,6); + String energy = value.substring(10,14); + String electric = value.substring(18,22); + String voltage = value.substring(26,30); + String powerValue = Float.valueOf((float)(Integer.valueOf(power,16))/10)+""; + String energyValue = Float.valueOf((float)(Integer.valueOf(energy,16))/100)+""; + String electricValue = Float.valueOf((float)(Integer.valueOf(electric,16))/1000)+""; + String voltageValue = Float.valueOf((float)(Integer.valueOf(voltage,16))/10)+""; + String message = "{\""+deviceId+"_Voltage_485\":"+voltageValue+",\""+deviceId+"_Current_485\":"+electricValue+",\""+deviceId+ + "_Instantaneous_power_485\":"+powerValue+",\""+deviceId+"_Amount_of_charge_485\":"+energyValue+"}"; + messageList.add(message); + } + break; + } + return messageList; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/schedule/OviphoneScheduler.java b/src/main/java/com/techsor/datacenter/sender/schedule/OviphoneScheduler.java new file mode 100644 index 0000000..249c444 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/schedule/OviphoneScheduler.java @@ -0,0 +1,23 @@ +package com.techsor.datacenter.sender.schedule; + +import com.techsor.datacenter.sender.processers.OviphoneHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + + +public class OviphoneScheduler { + private static final Logger logger = LoggerFactory.getLogger(OviphoneScheduler.class); + @Resource + private OviphoneHelper oviphoneHelper; + + @Scheduled(cron = "0/10 * * * * ? ") + public void executeTimelyOviphone(){ + logger.debug("Execute timely oviphoneHelper starting"); + oviphoneHelper.send(); + logger.debug("Execute timely oviphoneHelper ending"); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/AlarmDataPushLambda.java b/src/main/java/com/techsor/datacenter/sender/service/AlarmDataPushLambda.java new file mode 100644 index 0000000..5e3b12a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/AlarmDataPushLambda.java @@ -0,0 +1,1247 @@ +package com.techsor.datacenter.sender.service; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.seratch.jslack.Slack; +import com.github.seratch.jslack.api.webhook.Payload; +import com.github.seratch.jslack.api.webhook.WebhookResponse; +import com.techsor.datacenter.sender.components.CommonOpt; +import com.techsor.datacenter.sender.components.SendMail; +import com.techsor.datacenter.sender.config.DataSourceContextHolder; +import com.techsor.datacenter.sender.constants.Constants; +import com.techsor.datacenter.sender.dao.DeviceAlertConfigDao; +import com.techsor.datacenter.sender.dao.DeviceAlertTemplateBindDao; +import com.techsor.datacenter.sender.dao.DeviceAlertTemplateDao; +import com.techsor.datacenter.sender.dao.DeviceDao; +import com.techsor.datacenter.sender.dao.MqttConfigDao; +import com.techsor.datacenter.sender.dto.DeviceInfoVO; +import com.techsor.datacenter.sender.dto.RedisAlarmDTO; +import com.techsor.datacenter.sender.dto.VariableBoundsDTO; +import com.techsor.datacenter.sender.entitiy.HttpResponse; +import com.techsor.datacenter.sender.entitiy.MqttConfigEntity; +import com.techsor.datacenter.sender.utils.DataUtils; +import com.techsor.datacenter.sender.utils.HttpUtil; + +import cn.hutool.core.collection.CollectionUtil; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.json.JSONException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import jakarta.annotation.Resource; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.time.Instant; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class AlarmDataPushLambda { + + @Resource + private SendMail sendMail; + private static final String REDIS_DEVICE_ALARM_KEY = "lambda_device_alarm"; + private static final String REDIS_DEVICE_CANCEL_ALARM_KEY = Constants.REDIS_DEVICE_CANCEL_ALARM_KEY; + + private static final String ALERT_ALARM_REDIS_PREFIX="alert:alert_alert"; + private static final String STATUS_ALARM = "alert"; + private static final String STATUS_ALERT_CANCEL = "alert_cancel"; + + private static final String STATUS_NORMAL="normal"; + private static final String ALERT_CANCEL_INFO = "【%s アラートメール】%s %s の%s解除を検知しました"; + private static final String ALERT_INFO = "【%s アラートメール】%s %s で%sしました"; + + private static final String TMPL_VALUE_REGEX = "\\{(.*?)\\}"; + + private static final int MAX_RETRIES = 3; //iotcore + private static final long RETRY_DELAY_MS = 2000; // iotcore + + @Resource + RestTemplate httpRestTemplate; + + + @Value("${business.query.push.info}") + private String businessQueryPushInfo; + + @Resource + RedisTemplate alramRedisTemplate; + + + @Value("${roid.alarm.url}") + private String roidAlarmUrl; + + + @Value("${roid.alarm.cancel.url}") + private String roidAlarmCancelUrl; + + +// @Value("${roid.authorization}") +// private String roidAuthorization; + + + @Value("${roid2.url}") + private String roid2Url; + +// @Value("${roid2.authorization}") +// private String roid2Authorization; + + @Value("${alarm.email.result.url}") + private String emailResultUrl; + + @Autowired + private CommonOpt commonOpt; + + @Resource + private MqttClient mqttClient; + + @Resource + private DeviceDao deviceDao; + + @Resource + private MqttConfigDao mqttConfigDao; + + @Resource + private DeviceAlertTemplateBindDao deviceAlertTemplateBindDao; + + + @Resource + private DeviceAlertTemplateDao deviceAlertTemplateDao; + + @Resource + private DeviceAlertConfigDao deviceAlertConfigDao; + /** + * 分发警报记录。根据设备ID和原始JSON数据,分析警报内容并进行相应的处理。 + * 如果原始JSON中包含需要传递的警报内容,则构造一个新的JSON对象,并根据警报状态添加相应的警报内容。 + * 然后,将这个新的JSON对象通过HTTP POST请求发送到一个指定的URL。 + * 如果响应为空,则不进行任何操作。 + * 如果存在需要转发的目标,则会根据响应数据中提供的URL列表进行数据转发。 + * 最后,调用handleEmailAndRoid方法处理邮件和Roid通知。 + * + * @param deviceId 设备ID + * @param rawJson 原始JSON字符串 + * @param filterTargetSets 过滤目标集合 + * @param alertTriggerExpression 告警表达式 + * @throws Exception 可能抛出的异常 + */ + /** + * 分发警报记录。根据设备ID和原始JSON数据,分析警报内容并进行相应的处理。 + * 如果原始JSON中包含需要传递的警报内容,则构造一个新的JSON对象,并根据警报状态添加相应的警报内容。 + * 然后,将这个新的JSON对象通过HTTP POST请求发送到一个指定的URL。 + * 如果响应为空,则不进行任何操作。 + * 如果存在需要转发的目标,则会根据响应数据中提供的URL列表进行数据转发。 + * 最后,调用handleEmailAndRoid方法处理邮件和Roid通知。 + * + * @param deviceId 设备ID + * @param rawJson 原始JSON字符串 + * @param filterTargetSets 过滤目标集合 + * @param alertTriggerExpression 告警表达式 + * @param type 类型 + * @param alertTemplateInfo + * @param deviceInfoVO + * @throws Exception 可能抛出的异常 + */ + public void dispatchAlarmRecord(String companyId, String deviceId, String rawJson, Set filterTargetSets, + String alertTriggerExpression, int type, JSONObject alertTemplateInfo, DeviceInfoVO deviceInfoVO) throws JsonProcessingException { + JSONObject currentRawJsonObject = JSON.parseObject(rawJson); + log.info("-----------------"); + log.info(rawJson); + log.info("-----------------"); + +// if (StringUtils.isNotEmpty(currentRawJsonObject.getString("rawData")) && StringUtils.isNotEmpty(currentRawJsonObject.getString("alertTemplateIds"))) { +// JSONObject postJsonObject = new JSONObject(); +// postJsonObject.put("deviceId", currentRawJsonObject.getString("deviceId")); +// postJsonObject.put("status", currentRawJsonObject.getString("status")); +// postJsonObject.put("companyId", companyId); +// if (StringUtils.isNotEmpty(currentRawJsonObject.getString("alertTemplateIds"))){ +// postJsonObject.put("alarmTmplIds", currentRawJsonObject.getString("alertTemplateIds").replace("[", "").replace("]", "")); +// } +// +// //根据告警模板获取转发信息和告警方式 +// String resp = HttpUtil.doPost(businessQueryPushInfo, postJsonObject.toString() , null); +// log.info("queryByDeviceId result:{}", resp); +// if (StringUtils.isEmpty(resp)) { +// return; +// } +// +// JSONObject respObject = JSONObject.parseObject(resp); +// +// //处理转发 +// handleTargetUrl(respObject, currentRawJsonObject); +// +// //处理告警 +// handleEmailAndRiod(companyId, currentRawJsonObject, respObject, filterTargetSets, alertTriggerExpression); +// } + + if (null != alertTemplateInfo && !alertTemplateInfo.isEmpty()) { + //处理告警 + log.info("parsedAlarmInfoListArray 3:{}", alertTemplateInfo); + handleEmailAndRiod(companyId, currentRawJsonObject, alertTemplateInfo, filterTargetSets, alertTriggerExpression, deviceInfoVO); + } + } + + public static void handleTargetUrl(JSONObject respObject, JSONObject currentRawJsonObject) { + if (currentRawJsonObject.containsKey("needTransfer") && "true".equalsIgnoreCase(currentRawJsonObject.getString("needTransfer"))) { + String targetData = respObject.getString("rawData"); + List targetUrlList = new ArrayList<>(); + + JSONArray dataArr = respObject.getJSONObject("data").getJSONArray("dataList"); + if (200 == respObject.getIntValue("code") && CollectionUtil.isNotEmpty(dataArr)) { + for (Object data : dataArr) { + String targetForwardParamsStr = JSONObject.parseObject(data.toString()).getString("targetForwardParams"); + if (StringUtils.isNotEmpty(targetForwardParamsStr)) { + JSONObject tfpObject = JSONObject.parseObject(targetForwardParamsStr); + targetUrlList.add(tfpObject.getString("url")); + } + } + } + + + if (CollectionUtil.isNotEmpty(targetUrlList)) { + for (String targetUrl : targetUrlList) { + if (!StringUtils.isEmpty(targetUrl)) { + try { + String pushResp = HttpUtil.doPost(targetUrl, targetData, null); + log.info("push alarm result:{}", pushResp); + } catch (Exception e) { + log.error("push alarm error", e); + } + } + } + } else { + log.info("alarm push url is null........."); + } + } + } + + private void handleEmailAndRiod(String companyId, JSONObject currentRawJsonObject, JSONObject respObject, Set filterTargetSets, String alertTriggerExpression, DeviceInfoVO deviceInfoVO) { + if (currentRawJsonObject.containsKey("status")) { + String deviceId = currentRawJsonObject.getString("deviceId"); + + JSONArray allEffectivePeriod = respObject.getJSONObject("data").getJSONArray("allEffectivePeriod"); + log.info("effective alarm period: {}", allEffectivePeriod.toString()); + + Long receive_ts = currentRawJsonObject.getLong("receive_ts"); + + String redisStatus = (String) this.alramRedisTemplate.opsForHash().get(REDIS_DEVICE_ALARM_KEY, deviceId); + log.info("redis device alarm status: {}", redisStatus); + + try { + String status = currentRawJsonObject.getString("status"); + //无效告警(告警有效期内重复告警、不在告警有效期内的告警),不进行任何操作,只记录日志 + boolean isInvalidAlarm = false; + + if (StringUtils.isEmpty(redisStatus)) { + RedisAlarmDTO redisAlarmDTO = new RedisAlarmDTO(); + int type = -1; + if (STATUS_ALARM.equalsIgnoreCase(status)) { + + this.alramRedisTemplate.opsForZSet().remove(REDIS_DEVICE_CANCEL_ALARM_KEY, deviceId); + + //核对所有的有效期,看看是否符合某个有效期 + if(!isInEffectivePeriod(receive_ts, allEffectivePeriod)) { + isInvalidAlarm = true; + } + + type = 1; + + if (!isInvalidAlarm) { + //roid + roid(companyId, receive_ts, currentRawJsonObject, type, redisAlarmDTO, deviceId, respObject, filterTargetSets, alertTriggerExpression, deviceInfoVO); + + redisAlarmDTO.setStatus(status); + this.alramRedisTemplate.opsForHash().put(REDIS_DEVICE_ALARM_KEY, deviceId, JSONObject.toJSONString(redisAlarmDTO)); + //email +// switchAlarmSendMail(companyId, receive_ts, currentRawJsonObject, type, deviceId, respObject, deviceInfoVO); + //slack、teams、email + sendAlarmNotifications(companyId, receive_ts, currentRawJsonObject, type, deviceId, respObject, deviceInfoVO); + //mqtt + iotcoreOpt(companyId, deviceId, currentRawJsonObject, deviceInfoVO, 1); + } + } else if (!STATUS_ALARM.equalsIgnoreCase(status)) { + + this.alramRedisTemplate.opsForZSet().add(REDIS_DEVICE_CANCEL_ALARM_KEY, deviceId, receive_ts); + + type = 2; + redisAlarmDTO.setStatus(status); + this.alramRedisTemplate.opsForHash().put(REDIS_DEVICE_ALARM_KEY, deviceId, JSONObject.toJSONString(redisAlarmDTO)); + } + + } else { + ObjectMapper objectMapper = new ObjectMapper(); + RedisAlarmDTO redisAlarmDTO = objectMapper.readValue(redisStatus, RedisAlarmDTO.class); + log.info("redis data already exists, new status: "+ status +", redis status:" + redisAlarmDTO.getStatus()); + int type = -1; + /**** 只有在上报的状态和redis里的不一致,才处理 ***/ + if (STATUS_ALARM.equalsIgnoreCase(status) && !STATUS_ALARM.equalsIgnoreCase(redisAlarmDTO.getStatus())) { + + this.alramRedisTemplate.opsForZSet().remove(REDIS_DEVICE_CANCEL_ALARM_KEY, deviceId); + + //核对所有的有效期,看看是否符合某个有效期 + if(!isInEffectivePeriod(receive_ts, allEffectivePeriod)) { + isInvalidAlarm = true; + } + + //告警 + type = 1; + } else if (!STATUS_ALARM.equalsIgnoreCase(status) && STATUS_ALARM.equalsIgnoreCase(redisAlarmDTO.getStatus())) { + + this.alramRedisTemplate.opsForZSet().add(REDIS_DEVICE_CANCEL_ALARM_KEY, deviceId, receive_ts); + + //恢复告警 + type = 2; + } else if (STATUS_ALARM.equalsIgnoreCase(status) && STATUS_ALARM.equalsIgnoreCase(redisAlarmDTO.getStatus())) { + + this.alramRedisTemplate.opsForZSet().remove(REDIS_DEVICE_CANCEL_ALARM_KEY, deviceId); + + isInvalidAlarm = true; + } + + if (!isInvalidAlarm) { + //roid + if(-1 != type) { + roid(companyId, receive_ts, currentRawJsonObject, type, redisAlarmDTO, deviceId, respObject, filterTargetSets, alertTriggerExpression, deviceInfoVO); + } + //设置redis状态 + redisAlarmDTO.setStatus(status); + this.alramRedisTemplate.opsForHash().put(REDIS_DEVICE_ALARM_KEY, deviceId, JSONObject.toJSONString(redisAlarmDTO)); + + if(-1 != type) { + //email +// switchAlarmSendMail(companyId, receive_ts, currentRawJsonObject, type, deviceId, respObject, deviceInfoVO); + //slack、teams、email + sendAlarmNotifications(companyId, receive_ts, currentRawJsonObject, type, deviceId, respObject, deviceInfoVO); + //mqtt + iotcoreOpt(companyId, deviceId, currentRawJsonObject, deviceInfoVO, 1); + } + } + } + + if (isInvalidAlarm) { + //统计邮件成功/失败数量 + JSONObject postJsonObject = new JSONObject(); + postJsonObject.put("companyId", companyId); + postJsonObject.put("errorMsg", "Invalid alarm"); + postJsonObject.put("deviceId", deviceId); + postJsonObject.put("logType", ""); + postJsonObject.put("detail", ""); + postJsonObject.put("status", ""); + postJsonObject.put("alertType", 3); + if (deviceInfoVO!=null){ + postJsonObject.put("buildingName", StringUtils.defaultIfEmpty(deviceInfoVO.getBuildingName(),"")); + postJsonObject.put("floorName", StringUtils.defaultIfEmpty(deviceInfoVO.getFloorName(),"")); + postJsonObject.put("deviceName", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceName(),"")); + postJsonObject.put("deviceSn", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceSn(),"")); + } + + HttpResponse resp = HttpUtil.doPostWithRespone(emailResultUrl, postJsonObject.toString() , null); + log.info("log email result: {}", JSON.toJSONString(resp)); + if (200 != resp.getCode()) { + log.error("count failed, body:{}, response msg:{}", postJsonObject.toString(), resp.getMsg()); + } + } + if (!STATUS_ALARM.equalsIgnoreCase(status)) { + String deleteRedisKey = ALERT_ALARM_REDIS_PREFIX + deviceId; + this.alramRedisTemplate.delete(deleteRedisKey); + } + +// iotcoreOpt(companyId, deviceId, currentRawJsonObject, deviceInfoVO, 2); + + } catch (Exception e) { + e.printStackTrace(); + log.error("handleEmailAndRiod error", e); + } + } + } + + public void sendAlarmNotifications(String companyId, Long receiveTs, JSONObject rawJson, int type, String deviceId, JSONObject respData, DeviceInfoVO deviceInfoVO) { + log.debug("sendAlarmNotifications respData: {}", respData); + JSONArray alarmList = respData.getJSONObject("data").getJSONArray("parsedAlarmInfoList"); + if(alarmList == null || alarmList.isEmpty()) return; + for(Object object: alarmList){ + try{ + JSONObject alarmObj = JSONObject.parseObject(object.toString()); + // 检查有效期 + if(type == 1 && !isInEffectivePeriod(receiveTs, alarmObj.getJSONArray("effectivePeriod"))){ + continue; + } + + String forwardType = alarmObj.getString("forwardType"); + + if(StringUtils.isBlank(forwardType)) continue; + + // 构造通知内容 + String alertContent = replaceValue(rawJson, alarmObj.getString("alertContent")); + log.debug("parseAlertContent: {}", alertContent); + String subject = getEmailSubject(type, rawJson, alarmObj); + String defaultMemo = (type == 2) ? "設備が正常に戻りました" : ""; + String sendHtml = getMemo(alertContent, defaultMemo); + // 各通道发送 + if(forwardType.toLowerCase().contains("slack")){ + handleSlackNotification(alarmObj, sendHtml, companyId, deviceId, subject, type, deviceInfoVO); + } + if(forwardType.toLowerCase().contains("teams")){ + handleTeamsNotification(alarmObj, sendHtml, companyId, deviceId, subject, type, deviceInfoVO); + } + if(forwardType.toLowerCase().contains("mail")){ + handleEmailNotification(alarmObj, sendHtml, companyId, deviceId, subject, type, deviceInfoVO); + } + } catch(Exception e){ + log.error("[sendAlarmNotifications Error] Exception: ", e); + } + } + } + + private void handleSlackNotification(JSONObject alarmObj, String content, String companyId, String deviceId, String subject, int type, DeviceInfoVO deviceInfoVO){ + String webhooks = alarmObj.getString("slackWebhooks"); + if(StringUtils.isBlank(webhooks)) return; + String msg = content.replace("
    ", "\n"); + String[] webhookList = webhooks.split(","); + String errorMsg = ""; + for(String webhook: webhookList){ + try{ + processSlackMsg(msg, webhook); + } catch(Exception e){ + log.error("send slack message error", e); + errorMsg = e.getMessage(); + } + } + logNotificationResult("slack", companyId, deviceId, type, deviceInfoVO, content, webhooks, errorMsg); + } + + private void handleTeamsNotification(JSONObject alarmObj, String content, String companyId, String deviceId, String subject, int type, DeviceInfoVO deviceInfoVO){ + String webhooks = alarmObj.getString("teamsWebhooks"); + if(StringUtils.isBlank(webhooks)) return; + String msg = """ + { + "@type": "MessageCard", + "@context": "http://schema.org/extensions", + "summary": "System Notification", + "themeColor": "FF0000", + "title": "🚨 {{SUMMARY}}", + "sections": [{ + "activityTitle": "{{CONTENT}}", + "markdown": true + }] + } + """.replace("{{SUMMARY}}", subject).replace("{{CONTENT}}", content); + String[] webhookList = webhooks.split(","); + String errorMsg = ""; + for(String webhook: webhookList){ + try{ + processTeamsMsg(msg, webhook); + }catch(Exception e){ + log.error("send teams message error", e); + errorMsg = e.getMessage(); + } + } + logNotificationResult("teams", companyId, deviceId, type, deviceInfoVO, content, webhooks, errorMsg); + } + + + private void handleEmailNotification(JSONObject alarmObj, String content, String companyId, String deviceId, String subject, int type, DeviceInfoVO deviceInfoVO){ + String emails = alarmObj.getString("alertRecipientMail"); + + if(StringUtils.isBlank(emails)) return; + + List < String > emailList = Arrays.asList(emails.split(",")); + String receiver = emailList.get(0); + String cc = String.join(",", emailList.subList(1, emailList.size())); + String errorMsg = ""; + try{ + new SendMail().sendMail(subject, content, receiver, cc); + } catch(Exception e) { + log.error("[SendMail Error] email send failed", e); + errorMsg = e.getMessage(); + } + logNotificationResult("mail", companyId, deviceId, type, deviceInfoVO, content, emails, errorMsg); + } + + + private void logNotificationResult(String logType, String companyId, String deviceId, int type, DeviceInfoVO deviceInfoVO, String content, String receiver, String errorMsg) { + JSONObject postJson = new JSONObject(); + postJson.put("companyId", companyId); + postJson.put("deviceId", deviceId); + postJson.put("logType", logType); + postJson.put("alertType", type); + postJson.put("status", StringUtils.isEmpty(errorMsg) ? "Succeeded" : "Failed"); + postJson.put("errorMsg", errorMsg); + if(deviceInfoVO != null) + { + postJson.put("buildingName", StringUtils.defaultIfEmpty(deviceInfoVO.getBuildingName(), "")); + postJson.put("floorName", StringUtils.defaultIfEmpty(deviceInfoVO.getFloorName(), "")); + postJson.put("deviceName", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceName(), "")); + postJson.put("deviceSn", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceSn(), "")); + } + JSONObject detail = new JSONObject(); + detail.put("content", content); + detail.put("receiver", receiver); + postJson.put("detail", detail.toString()); + HttpResponse resp = HttpUtil.doPostWithRespone(emailResultUrl, postJson.toString(), null); + log.info("log {} result: {}", logType, JSON.toJSONString(resp)); + if(resp.getCode() != 200) + { + log.error("log failed, postBody:{}, msg:{}", postJson.toString(), resp.getMsg()); + } + } + + +// private void slackTeamsSend(String companyId, Long receive_ts, JSONObject currentRawJsonObject, int type, +// String deviceId, JSONObject respData, DeviceInfoVO deviceInfoVO) { +// log.debug("slackTeamsSend respData:"+respData); +// JSONArray listArray = respData.getJSONObject("data").getJSONArray("parsedAlarmInfoList"); +// if (null != listArray && listArray.size() > 0) { +// for (Object object : listArray) { +// try { +// JSONObject respObject = JSONObject.parseObject(object.toString()); +// +// //单个的模板在不在有效期 +// if(1 == type && !isInEffectivePeriod(receive_ts, respObject.getJSONArray("effectivePeriod"))) { +// continue; +// } +// +// if (!respObject.containsKey("forwardType")) { +// continue; +// } +// +// String forwardType = respObject.getString("forwardType").toLowerCase(); +// +// if(!forwardType.contains("teams") && !forwardType.contains("slack")) { +// continue; +// } +// +// String parseAlertContent = replaceValue(currentRawJsonObject, respObject.getString("alertContent")); +// log.debug("slackTeamsSend parseAlertContent:"+parseAlertContent); +// String subject = getEmailSubject(type, currentRawJsonObject, respObject); +// +// String defaultMemo = ""; +// if (2 == type) { +// defaultMemo = "設備が正常に戻りました"; +// } +// String sendHtml = getMemo(parseAlertContent, defaultMemo); +// +// String errorMsg = ""; +// String logType = ""; +// String receiver = ""; +// try { +// if(forwardType.contains("slack") && StringUtils.isNotBlank(respObject.getString("slackWebhooks"))) { +// logType = "slack"; +// receiver = respObject.getString("slackWebhooks"); +// String msg = sendHtml.replace("
    ", "\n"); +// String[] webhookList = respObject.getString("slackWebhooks").split(","); +// for (String webhook : webhookList) { +// try { +// processSlackMsg(msg, webhook); +// } catch (Exception e) { +// log.error("send slack message error", e); +// errorMsg = e.getMessage(); +// } +// } +// } +// if(forwardType.contains("teams") && StringUtils.isNotBlank(respObject.getString("teamsWebhooks"))) { +// logType = "teams"; +// receiver = respObject.getString("teamsWebhooks"); +// String msg = """ +// { +// "@type": "MessageCard", +// "@context": "http://schema.org/extensions", +// "summary": "System Notification", +// "themeColor": "FF0000", +// "title": "🚨 {{SUMMARY}}", +// "sections": [{ +// "activityTitle": "{{CONTENT}}", +// "markdown": true +// }] +// } +// """.replace("{{SUMMARY}}", subject).replace("{{CONTENT}}", sendHtml); +// String[] webhookList = respObject.getString("teamsWebhooks").split(","); +// for (String webhook : webhookList) { +// try { +// processTeamsMsg(msg, webhook); +// } catch (Exception e) { +// log.error("send teams message error", e); +// errorMsg = e.getMessage(); +// } +// } +// } +// } catch (Exception e) { +// log.error("[slackTeamsSend Error] sent error", e); +// errorMsg = e.getMessage(); +// } +// //统计邮件成功/失败数量 +// JSONObject postJsonObject = new JSONObject(); +// postJsonObject.put("companyId", companyId); +// postJsonObject.put("errorMsg", errorMsg); +// postJsonObject.put("deviceId", deviceId); +// postJsonObject.put("logType", logType); +// postJsonObject.put("alertType", type); +// if (deviceInfoVO!=null){ +// postJsonObject.put("buildingName", StringUtils.defaultIfEmpty(deviceInfoVO.getBuildingName(),"")); +// postJsonObject.put("floorName", StringUtils.defaultIfEmpty(deviceInfoVO.getFloorName(),"")); +// postJsonObject.put("deviceName", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceName(),"")); +// postJsonObject.put("deviceSn", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceSn(),"")); +// } +// JSONObject detail = new JSONObject(); +// detail.put("content", sendHtml); +// detail.put("receiver", receiver); +// postJsonObject.put("detail", detail.toString()); +// +// if (StringUtils.isEmpty(errorMsg)) { +// postJsonObject.put("status", "Succeeded"); +// } else { +// postJsonObject.put("status", "Failed"); +// } +// HttpResponse resp = HttpUtil.doPostWithRespone(emailResultUrl, postJsonObject.toString() , null); +// log.info("log slackTeamsSend result: {}", JSON.toJSONString(resp)); +// if (200 != resp.getCode()) { +// log.error("count failed, body:{}, response msg:{}", postJsonObject.toString(), resp.getMsg()); +// } +// } catch (Exception e) { +// log.error("[slackTeamsSend Error] slackTeamsSend error: {}", e); +// } +// } +// } +// } + + private void processTeamsMsg(String msg, String webhook) throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(webhook)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(msg)) + .build(); + log.info("teams发送消息:{}, 对象:{}", msg, webhook); + java.net.http.HttpResponse response = client.send(request, java.net.http.HttpResponse.BodyHandlers.ofString()); + log.info("teams发送消息返回:{}", JSONObject.toJSONString(response)); + } + + private static void processSlackMsg(String msg, String hookurl) throws IOException { + Payload payload = Payload.builder() + .text(msg) + .build(); + log.info("slack发送消息:{}, 对象:{}", msg, hookurl); + WebhookResponse webhookResponse = Slack.getInstance().send(hookurl, payload); + log.info("slack发送消息返回:{}", JSONObject.toJSONString(webhookResponse)); + } + + public void iotcoreOpt(String companyId, String deviceId, JSONObject currentRawJsonObject, DeviceInfoVO deviceInfoVO, int purposeType) { + String topCompanyId= commonOpt.getTopCompanyId(deviceId); + if (topCompanyId==null){ + return; + } + + DataSourceContextHolder.clearCurrentDataSourceKey(); + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + log.info("Use datasource for company:"+topCompanyId); + + log.info("get iotcore info:{}, {}", deviceId, purposeType); + List mqttInfo = mqttConfigDao.getByDeviceId(deviceId, purposeType); + if (!CollectionUtil.isEmpty(mqttInfo)) { + + if (null == deviceInfoVO) { + return; + } + + if (1 == purposeType) { + deviceInfoVO.setAlertStatus(currentRawJsonObject.getString("status").equalsIgnoreCase(STATUS_ALARM) ? STATUS_ALARM : STATUS_ALERT_CANCEL); + } else { + deviceInfoVO.setAlertStatus(currentRawJsonObject.getString("status")); + } + deviceInfoVO.setLatestRawData(currentRawJsonObject.getString("rawData")); + deviceInfoVO.setLatestDataTime(currentRawJsonObject.getLong("receive_ts")); + JSONObject dataObject = JSON.parseObject(currentRawJsonObject.getString("rawData")); + // 遍历所有键值对 + List valueList = new ArrayList<>(); + for (String key : dataObject.keySet()) { + valueList.add(dataObject.get(key)); + } + deviceInfoVO.setDataValue(StringUtils.join(valueList, ",")); + + String content = JSON.toJSONString(deviceInfoVO); + + for (MqttConfigEntity mqttConfigEntity : mqttInfo) { + MqttMessage message = new MqttMessage(content.getBytes()); + message.setQos(1); + String topic = mqttConfigEntity.getMqttTopic(); + + int attempts = 0; + boolean isPublished = false; + while (!isPublished && attempts < MAX_RETRIES) { + try { + // + if (!mqttClient.isConnected()) { + log.warn("MQTT client not connected, attempting to reconnect..."); + mqttClient.reconnect(); + log.info("MQTT client reconnect result: " + mqttClient.isConnected()); + } + + mqttClient.publish(topic, message); // 发布消息 + isPublished = true; // 成功发布后设置为 true + + log.info("Message published successfully to topic: {}, message: {}", topic, content); + + } catch (MqttException e) { + attempts++; + log.error("Failed to publish message, attempt " + attempts, e); + + if (attempts >= MAX_RETRIES) { + log.error("Max retries reached, message could not be published."); + break; + } + // + try { + Thread.sleep(RETRY_DELAY_MS * attempts); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + log.error("Thread interrupted during retry delay", ie); + } + } + } + } + } + } + + public DeviceInfoVO queryDeviceInfoByDeviceId(String deviceId) { + List deviceInfos = deviceDao.getDeviceInfo(deviceId); + if (CollectionUtil.isEmpty(deviceInfos)) { + return null; + } + return deviceInfos.get(0); + } + + private boolean isInEffectivePeriod(Long receive_ts, JSONArray allEffectivePeriod) { + if (null == allEffectivePeriod || allEffectivePeriod.size() == 0) { + return true; + } + if (null == receive_ts) { + receive_ts = System.currentTimeMillis(); + } + + boolean inTimeRange = false; + try { + // Define time period array +// String[] timeRanges = {"08:30-14:30", "22:00-03:00"}; + // Convert timestamp to Japanese time + Instant instant = Instant.ofEpochMilli(receive_ts); + ZonedDateTime nowInJapan = instant.atZone(ZoneId.of("Asia/Tokyo")); + LocalTime currentTime = nowInJapan.toLocalTime(); + + // Determine if the time falls within the time period + for (Object timeRange : allEffectivePeriod) { + //noSet表示没有设置有效期,就是全部时间都有效,这个noSet是在businessQueryPushInfo接口那边设置的 + if ("noSet".equals(timeRange.toString())) { + inTimeRange = true; + break; + } + String[] times = timeRange.toString().split("-"); + LocalTime startTime = LocalTime.parse(times[0]); + LocalTime endTime = LocalTime.parse(times[1]); + if (judgeInTimeRange(currentTime, startTime, endTime)) { + inTimeRange = true; + break; + } + } + } catch (Exception e) { + log.error("isInEffectivePeriod error", e); + return true; + } + + return inTimeRange; + } + + private boolean judgeInTimeRange(LocalTime currentTime, LocalTime startTime, LocalTime endTime) { + if (startTime.isBefore(endTime)) { + return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime); + } else { // Time period spanning across different days + return !currentTime.isBefore(startTime) || !currentTime.isAfter(endTime); + } + } + + private void roid(String companyId, Long receive_ts, JSONObject currentRawJsonObject, int type, RedisAlarmDTO redisAlarmDTO, String deviceId, JSONObject respData, Set filterTargetSets, String alertTriggerExpression, DeviceInfoVO deviceInfoVO) { + JSONArray listArray = respData.getJSONObject("data").getJSONArray("parsedAlarmInfoList"); + if (null != listArray && listArray.size() > 0) { + for (Object object : listArray) { + JSONObject respObject = JSONObject.parseObject(object.toString()); + + if(1 == type && !isInEffectivePeriod(receive_ts, respObject.getJSONArray("effectivePeriod"))) { + continue; + } + + //forwardType只有mail,roid1,roid2 + if (!respObject.containsKey("forwardType") || !respObject.getString("forwardType").toLowerCase().contains("roid")) { + continue; + } + + String forwardType = respObject.getString("forwardType").toLowerCase(); + + String parseAlertContent = replaceValue(currentRawJsonObject, respObject.getString("alertContent")); + log.info("parsedAlarmInfoListArray 4:{}", parseAlertContent); + + String emailSubject = getEmailSubject(type, currentRawJsonObject, respObject); + + switch (forwardType) { + case "roid1": + handleRoid1(companyId, respData, deviceId, parseAlertContent, emailSubject, currentRawJsonObject, type, redisAlarmDTO, deviceInfoVO); + break; + case "roid2": + handleRoid2(companyId, respData, deviceId, currentRawJsonObject, alertTriggerExpression, filterTargetSets, type, deviceInfoVO); + break; + default: + break; + } + } + } + } + + + private void handleRoid2(String companyId, JSONObject respData, String deviceId, JSONObject currentRawJsonObject, String alertTriggerExpression, + Set filterTargetSets, int type, DeviceInfoVO deviceInfoVO) { + filterTargetSets = filterTargetSets.stream() + .map(iter -> iter.replaceAll("[\\[\\]'\" ]", "")) + .collect(Collectors.toSet()); + + String targetIdList = currentRawJsonObject.getString("targetId"); + if (StringUtils.isEmpty(targetIdList)) { + return; + } + + Map paramsMap = JSON.parseObject(targetIdList, new TypeReference>() {}); + if (paramsMap.isEmpty()) { + return; + } + + Map rawDataMap = JSON.parseObject(currentRawJsonObject.getString("rawData"), new TypeReference>() {}); + VariableBoundsDTO variableBoundsDTO = DataUtils.extractMinMax(alertTriggerExpression); + + Set finalFilterTargetSets = filterTargetSets; + paramsMap.forEach((iterKey, standardTargetId) -> { + iterKey = iterKey.replaceAll("[\\[\\]'\" ]", ""); + Object targetIdValue = MapUtils.getObject(rawDataMap, iterKey); + + if (finalFilterTargetSets.contains(iterKey)) { + currentRawJsonObject.put("forwardValue", targetIdValue); + currentRawJsonObject.put("targetId", standardTargetId); + handleRoid2Data(companyId, deviceId, respData, currentRawJsonObject, type, standardTargetId, rawDataMap, variableBoundsDTO, deviceInfoVO); + } + }); + } + + + public void handleRoid2Data(String companyId, String deviceId, JSONObject respData, JSONObject keys, int type, Object targetId, Map rawDataMap, VariableBoundsDTO variableBoundsDTO, DeviceInfoVO deviceInfoVO) { + if (!keys.containsKey("forwardTypeList") || Objects.isNull(keys.getJSONArray("forwardTypeList"))) { + return; + } + + List forwardTypeList = keys.getJSONArray("forwardTypeList").toJavaList(String.class); + + if (forwardTypeList.stream().noneMatch(ft -> ft.equalsIgnoreCase("roid2"))) { + return; + } + +// String replaceRoid2Url = org.apache.commons.lang3.StringUtils.replace(roid2Url, "{targetId}", String.valueOf(targetId)); + String replaceRoid2Url = respData.getJSONObject("data").getString("thirdApiHost") + StringUtils.replace(roid2Url, "{targetId}", String.valueOf(targetId)); + HashMap headerMap = new HashMap<>(); + headerMap.put("Content-Type", "application/json;charset=UTF-8"); + headerMap.put("access-control-allow-credentials", "True"); + headerMap.put("X-Requested-With", "XMLHttpRequest"); + headerMap.put("Authorization", respData.getJSONObject("data").getString("bearerToken")); + headerMap.put("Accept", "application/json, text/plain, /"); + + if (2 == type) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("status", 0); + jsonObject.put("value", keys.get("forwardValue")); + String formattedUpperBound = variableBoundsDTO.getUpperBound() == null ? null : String.format("%.5f", variableBoundsDTO.getUpperBound()); + String formattedLowerBound = variableBoundsDTO.getLowerBound() == null ? null : String.format("%.5f", variableBoundsDTO.getLowerBound()); + + jsonObject.put("normal_upper",formattedUpperBound); + jsonObject.put("normal_lower",formattedLowerBound); + log.info("roid2 alarm api req : {}", JSON.toJSONString(jsonObject)); + HttpResponse httpResponse = HttpUtil.doPostWithRespone(replaceRoid2Url, jsonObject.toString(), headerMap); + log.info("roid2 alarm api resp : {}", JSON.toJSONString(httpResponse)); + + logRoidResult(companyId, deviceId, httpResponse, replaceRoid2Url, jsonObject.toString(), "roid2", type, deviceInfoVO); + + } else if (1 == type) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("status", 1); + jsonObject.put("value",keys.get("forwardValue")); + jsonObject.put("normal_upper",variableBoundsDTO.getUpperBound()); + jsonObject.put("normal_lower",variableBoundsDTO.getLowerBound()); + log.info("roid2 alarm api req : {}", JSON.toJSONString(jsonObject)); + HttpResponse httpResponse = HttpUtil.doPostWithRespone(replaceRoid2Url, jsonObject.toString(), headerMap); + log.info("roid2 cancel alarm api resp : {}", JSON.toJSONString(httpResponse)); + + logRoidResult(companyId, deviceId, httpResponse, replaceRoid2Url, jsonObject.toString(), "roid2", type, deviceInfoVO); + } + } + + + private void logRoidResult(String companyId, String deviceId, HttpResponse httpResponse, String url, + String body, String logType, int type, DeviceInfoVO deviceInfoVO) { + //统计邮件成功/失败数量 + JSONObject postJsonObject = new JSONObject(); + postJsonObject.put("companyId", companyId); + postJsonObject.put("errorMsg", httpResponse.getMsg()); + postJsonObject.put("deviceId", deviceId); + postJsonObject.put("logType", logType); + postJsonObject.put("alertType", type); + if (deviceInfoVO!=null){ + postJsonObject.put("buildingName", StringUtils.defaultIfEmpty(deviceInfoVO.getBuildingName(),"")); + postJsonObject.put("floorName", StringUtils.defaultIfEmpty(deviceInfoVO.getFloorName(),"")); + postJsonObject.put("deviceName", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceName(),"")); + postJsonObject.put("deviceSn", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceSn(),"")); + } + + + JSONObject detail = new JSONObject(); + detail.put("content", body); + detail.put("url", url); + postJsonObject.put("detail", detail.toString()); + + if (200 == httpResponse.getCode()) { + postJsonObject.put("status", "Succeeded"); + } else { + postJsonObject.put("status", "Failed"); + } + HttpResponse resp = HttpUtil.doPostWithRespone(emailResultUrl, postJsonObject.toString() , null); + log.info("log roid result: {}", JSON.toJSONString(resp)); + if (200 != resp.getCode()) { + log.error("count failed, body:{}, response msg:{}", postJsonObject.toString(), resp.getMsg()); + } + } + + private void handleRoid1(String companyId, JSONObject respData, String deviceId, String parseAlertContent, String emailSubject, JSONObject currentRawJsonObject, int type, + RedisAlarmDTO redisAlarmDTO, DeviceInfoVO deviceInfoVO) { + HashMap headerMap = new HashMap<>(); + headerMap.put("Content-Type", "application/json;charset=UTF-8"); + headerMap.put("access-control-allow-credentials", "True"); + headerMap.put("X-Requested-With", "XMLHttpRequest"); + headerMap.put("Authorization", respData.getJSONObject("data").getString("bearerToken")); + headerMap.put("Accept", "application/json, text/plain, /"); + if (1 == type) { + String rawTargetId = currentRawJsonObject.getString("targetId"); + if (StringUtils.isEmpty(rawTargetId)) { + return; + } + Map targetIdMap = JSON.parseObject(rawTargetId, new TypeReference>() {}); + if (targetIdMap.isEmpty()) { + return; + } + String targetId = ""; + for (String key : targetIdMap.keySet()) { + targetId = targetIdMap.get(key).toString(); + } + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("problem_report_category_id", currentRawJsonObject.getLong("problemReportCategoryId")); + jsonObject.put("target_id", Long.valueOf(targetId)); + jsonObject.put("memo", getMemo(parseAlertContent, "").replace("
    ", " ")); + if(currentRawJsonObject.containsKey("buildingId")) { + jsonObject.put("building_id", currentRawJsonObject.getLong("buildingId")); + } + + log.info("roid1 alarm api req : {}", jsonObject.toString()); + String roid1AlarmUrl = respData.getJSONObject("data").getString("thirdApiHost") + roidAlarmUrl; + HttpResponse httpResponse = HttpUtil.doPostWithRespone(roid1AlarmUrl, jsonObject.toString(), headerMap); + log.info("roid1 alarm api resp : {}", JSON.toJSONString(httpResponse)); + + logRoidResult(companyId, deviceId, httpResponse, roid1AlarmUrl, jsonObject.toString(), "roid1", type, deviceInfoVO); + + //redis设置状态和id + if (!StringUtils.isEmpty(httpResponse.getMsg())) { + JSONObject roidRespObject = JSONObject.parseObject(httpResponse.getMsg()); + if (roidRespObject.containsKey("id")) { + redisAlarmDTO.setProblemReportId(roidRespObject.getLongValue("id")); + } + } + } else if (2 == type) { + if (null != redisAlarmDTO && null != redisAlarmDTO.getProblemReportId()) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("problem_report_id", redisAlarmDTO.getProblemReportId()); + jsonObject.put("memo", getMemo(parseAlertContent, emailSubject).replace("
    ", " ")); + + String roid1AlarmCancelUrl = respData.getJSONObject("data").getString("thirdApiHost") + roidAlarmCancelUrl; + HttpResponse httpResponse = HttpUtil.doPostWithRespone(roid1AlarmCancelUrl, jsonObject.toString(), headerMap); + log.info("roid1 cancel alarm api resp : {}", JSON.toJSONString(httpResponse)); + + logRoidResult(companyId, deviceId, httpResponse, roid1AlarmCancelUrl, jsonObject.toString(), "roid1", type, deviceInfoVO); + } + } + } + +// private void switchAlarmSendMail(String companyId, Long receive_ts, JSONObject currentRawJsonObject, int type, String deviceId, JSONObject respData, DeviceInfoVO deviceInfoVO) { +// log.debug("switchAlarmSendMail respData:"+respData); +// JSONArray listArray = respData.getJSONObject("data").getJSONArray("parsedAlarmInfoList"); +// if (null != listArray && listArray.size() > 0) { +// for (Object object : listArray) { +// try { +// JSONObject respObject = JSONObject.parseObject(object.toString()); +// +// //单个的模板在不在有效期 +// if(1 == type && !isInEffectivePeriod(receive_ts, respObject.getJSONArray("effectivePeriod"))) { +// continue; +// } +// +// if (!respObject.containsKey("forwardType") || +// (!respObject.getString("forwardType").contains("mail") && !respObject.getString("forwardType").contains("MAIL"))) { +// continue; +// } +// +// String emails = respObject.getString("alertRecipientMail"); +// if (StringUtils.isEmpty(emails)) { +// continue; +// } +// +// String parseAlertContent = replaceValue(currentRawJsonObject, respObject.getString("alertContent")); +// log.debug("switchAlarmSendMail parseAlertContent:"+parseAlertContent); +// String subject = getEmailSubject(type, currentRawJsonObject, respObject); +// +// String defaultMemo = ""; +// if (2 == type) { +// defaultMemo = "設備が正常に戻りました"; +// } +// +// String sendHtml = getMemo(parseAlertContent, defaultMemo); +// List emailList = Arrays.asList(emails.contains(",") ? StringUtils.split(emails, ",") : new String[]{emails}); +// List sendList = new ArrayList(emailList); +// //收件人 +// String receiver = sendList.get(0); +// //抄送人 +// sendList.remove(receiver); +// String cc = StringUtils.join(sendList, ","); +// +// String errorMsg = ""; +// try { +// SendMail sendMail = new SendMail(); +// sendMail.sendMail(subject, sendHtml, receiver, cc); +// } catch (Exception e) { +// log.error("[SendMail Error] mail sent error", e); +// errorMsg = e.getMessage(); +// } +// //统计邮件成功/失败数量 +// JSONObject postJsonObject = new JSONObject(); +// postJsonObject.put("companyId", companyId); +// postJsonObject.put("errorMsg", errorMsg); +// postJsonObject.put("deviceId", deviceId); +// postJsonObject.put("logType", "mail"); +// postJsonObject.put("alertType", type); +// if (deviceInfoVO!=null){ +// postJsonObject.put("buildingName", StringUtils.defaultIfEmpty(deviceInfoVO.getBuildingName(),"")); +// postJsonObject.put("floorName", StringUtils.defaultIfEmpty(deviceInfoVO.getFloorName(),"")); +// postJsonObject.put("deviceName", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceName(),"")); +// postJsonObject.put("deviceSn", StringUtils.defaultIfEmpty(deviceInfoVO.getDeviceSn(),"")); +// } +// JSONObject detail = new JSONObject(); +// detail.put("content", sendHtml); +// detail.put("receiver", emails); +// postJsonObject.put("detail", detail.toString()); +// +// if (StringUtils.isEmpty(errorMsg)) { +// postJsonObject.put("status", "Succeeded"); +// } else { +// postJsonObject.put("status", "Failed"); +// } +// HttpResponse resp = HttpUtil.doPostWithRespone(emailResultUrl, postJsonObject.toString() , null); +// log.info("log email result: {}", JSON.toJSONString(resp)); +// if (200 != resp.getCode()) { +// log.error("count failed, body:{}, response msg:{}", postJsonObject.toString(), resp.getMsg()); +// } +// } catch (Exception e) { +// log.error("[SendMail Error] switchAlarmSendMail error: {}", e); +// } +// } +// } +// } + + private String getEmailSubject(int type, JSONObject currentRawJsonObject, JSONObject alertForwardConfig) { + if (StringUtils.isNotEmpty(alertForwardConfig.getString("alertTitle"))) { + return alertForwardConfig.getString("alertTitle"); + } else { + String alarmType = alertForwardConfig.getJSONObject("data").getJSONObject("deviceAlertConfig").getString("type"); + + String buildingName = ""; + if (StringUtils.isNotBlank(currentRawJsonObject.getString("buildingInfo"))) { + JSONObject obj = JSONObject.parseObject(currentRawJsonObject.getString("buildingInfo")); + buildingName = obj.getString("name"); + } + + String floorName = ""; + if (StringUtils.isNotBlank(currentRawJsonObject.getString("floorInfo"))) { + JSONObject obj = JSONObject.parseObject(currentRawJsonObject.getString("floorInfo")); + floorName = obj.getString("name"); + } + + String spaceName = ""; + if (StringUtils.isNotBlank(currentRawJsonObject.getString("spaceInfo"))) { + JSONObject obj = JSONObject.parseObject(currentRawJsonObject.getString("spaceInfo")); + spaceName = obj.getString("name"); + } + if (1 == type) { + return String.format(ALERT_INFO, buildingName, floorName, spaceName, alarmType); + } else if (2 == type) { + return String.format(ALERT_CANCEL_INFO, buildingName, floorName, spaceName, alarmType); + } + return ""; + } + } + + + private String replaceValue(JSONObject keys, String alertContent) { + if (StringUtils.isNotBlank(keys.getString("rawData"))) { + String rawData = keys.getString("rawData"); + + JSONObject rawDataObject = JSONObject.parseObject(rawData); + + Pattern pattern = Pattern.compile(TMPL_VALUE_REGEX); + Matcher matcher = pattern.matcher(alertContent); + while (matcher.find()) { + String match = matcher.group(1); // 获取匹配到的内容 + for (String key : rawDataObject.keySet()) { + if (key.endsWith(match)) { + alertContent = alertContent.replace("{" + match + "}", rawDataObject.getString(key)); + } + } + } + //在这里特殊处理振动传感器的告警变量 + try { + if (alertContent.contains("{zaiot_fault_content}")){ + alertContent = process_zaiot_alert_content(alertContent,keys.get("rawData").toString(),"fault"); + } + if (alertContent.contains("{zaiot_state_content}")){ + alertContent = process_zaiot_alert_content(alertContent,keys.get("rawData").toString(),"state"); + } + } catch (Exception e) { + log.error("replaceValue error: {}", e); + } + } + return alertContent; + } + + /** + * zaiot传感器告警变量单独处理 + * 变量:{zaiot_state_content} {zaiot_fault_content} + * @param alertContent + * @param rawData + * @param type + * @return + * @throws JSONException + */ + private String process_zaiot_alert_content(String alertContent,String rawData,String type) throws JSONException { + log.info("process_zaiot_alert_content: {}", alertContent); + + org.json.JSONObject jsonObject = new org.json.JSONObject(rawData); + Integer stateValue = null; + Integer faultValue = null; + + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + + if (key.endsWith("_alarm_state")) { + stateValue = jsonObject.getInt(key); + } else if (key.endsWith("_alarm_fault")) { + faultValue = jsonObject.getInt(key); + } + } + System.out.println(stateValue); + System.out.println(faultValue); + + if (type.equals("state")){ + String stateContent = ""; + switch(stateValue){ + case 2: + stateContent="危険:(重大な異常が検出され、すぐに停止や点検が必要な状態。)"; + break; + case 3: + stateContent="警告:(振動値が上がる傾向がある。異常の兆候が明確に現れており、近いうちに故障や性能劣化が起きる可能性がある。)"; + break; + case 4: + stateContent="注意:(軽微な異常や傾向が検出されている。すぐに危険ではないが、状態の監視を継続し、必要に応じてメンテナンスを検討する段階。)"; + break; + case 5: + stateContent="停止:(機器が停止、センサーのデータ収集が行われていない状態。定期点検や保守中の場合も含まれる。)"; + break; + default: + stateContent=""; + } + log.info("stateContent: {}", stateContent); + alertContent = alertContent.replace("{zaiot_state_content}",stateContent); + + } + if (type.equals("fault")){ + String faultContent = ""; + switch(faultValue){ + case 1: + faultContent="ローターのアンバランス:(回転体の質量が均等でないため、回転中に偏心が生じ、周期的な振動が発生する状態です。軸受や構造部品への負荷が増加します。)"; + break; + case 2: + faultContent="ローターの芯ずれ:(モーターとポンプなどの回転軸の中心が正しく合っておらず、エネルギー損失や異常振動の原因となります)"; + break; + case 3: + faultContent="緩み:(機械部品のボルトや固定部に緩みが生じており、がたつきや衝撃的な振動が観測される状態です。構造破損の前兆になる場合があります)"; + break; + case 4: + faultContent="キャビテーション(気泡の発生):(キャビテーションとは、ポンプや配管内の液体が急激な圧力低下によって蒸気泡を発生させ、それが高圧域で急激に潰れる現象です。この気泡の崩壊時に衝撃波が発生し、インペラや配管の表面を損傷(穴あき、剥離)させることがあります。主な発生要因:ポンプ吸込圧力が低すぎ;ポンプの過負荷運転)"; + break; + case 5: + faultContent="潤滑不良:(軸受などに十分な潤滑がされておらず、摩擦が増大して振動や温度が上昇します。早期の損傷につながります)"; + break; + case 6: + faultContent="転がり軸受の故障:(玉軸受やころ軸受などの内部部品(外輪・内輪・転動体・保持器など)に損傷が発生しており、特定の周波数帯に異常振動が出現します)"; + break; + case 7: + faultContent="ギアの故障:(ギアの歯こぼれや摩耗などにより、振動やノイズが発生している状態です。通常はギアメッシュ周波数に異常が現れます)"; + break; + case 8: + faultContent="温度が高すぎる:(設備の表面温度が許容範囲を超えて上昇している状態で、過負荷や冷却不良の可能性があります)"; + break; + case 9: + faultContent="温度上昇が急速:(短時間で温度が急上昇しており、突発的な異常(例えば潤滑切れ、焼き付きなど)の兆候が考えられます)"; + break; + case 10: + faultContent="不明な故障:(既知のパターンに該当しないが、異常な振動や温度の兆候が検出されている状態です。詳細な点検が推奨されます)"; + break; + default: + faultContent=""; + } + log.info("faultContent: {}", faultContent); + alertContent = alertContent.replace("{zaiot_fault_content}",faultContent); + } + return alertContent; + } + + /** + * 生成邮件正文内容。如果指定了警报内容,则使用该内容,否则使用默认备忘录。 + * + * @param parseAlertContent 解析后的警报内容 + * @param defaultMemo 默认备忘录内容 + * @return 生成的邮件正文内容 + */ + + private String getMemo(String parseAlertContent, String defaultMemo) { + return StringUtils.isEmpty(parseAlertContent) ? defaultMemo : parseAlertContent; + } + +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/service/AlarmDataPushNoAlertTemplateIds.java b/src/main/java/com/techsor/datacenter/sender/service/AlarmDataPushNoAlertTemplateIds.java new file mode 100644 index 0000000..3663620 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/AlarmDataPushNoAlertTemplateIds.java @@ -0,0 +1,158 @@ +package com.techsor.datacenter.sender.service; + +import com.alibaba.fastjson2.JSONObject; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.techsor.datacenter.sender.components.CommonOpt; +import com.techsor.datacenter.sender.constants.Constants; +import com.techsor.datacenter.sender.dto.RedisAlarmDTO; +import com.techsor.datacenter.sender.utils.HttpUtil; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import jakarta.annotation.Resource; +import java.util.Map; + +@Component +public class AlarmDataPushNoAlertTemplateIds { + + private static final String REDIS_DEVICE_ALARM_KEY = Constants.REDIS_DEVICE_ALARM_KEY; + private static final String STATUS_ALARM = Constants.STATUS_ALARM; + private static final String STATUS_ALERT_CANCEL = Constants.STATUS_ALERT_CANCEL; + + + @Resource + private Constants constants; + + @Resource + private HashOperations hashOperations; + + + @Resource + CommonOpt commonOpt; + + //偷下懒,就这么写吧,不用兼容了 + public void handleAlarm(Map keys) { + if (null != keys && !keys.isEmpty() && keys.containsKey("rawData") && !StringUtils.isEmpty(keys.get("rawData").toString())) { +// String resp = HttpUtil.doGet(QUERY_PUSH_INFO + keys.get("deviceId").getS(), null); + JSONObject postJsonObject = new JSONObject(); + postJsonObject.put("deviceId", keys.get("deviceId")); + String status = keys.get("status").toString(); + if (STATUS_ALARM.equalsIgnoreCase(status)) { + postJsonObject.put("alertContent", keys.containsKey("alertContent") ? keys.get("alertContent").toString() : ""); + } else if (STATUS_ALERT_CANCEL.equalsIgnoreCase(status)) { + postJsonObject.put("alertContent", keys.containsKey("alertCancelContent") ? keys.get("alertCancelContent").toString() : ""); + } + postJsonObject.put("alertTitle", keys.containsKey("alertTitle") ? keys.get("alertTitle").toString() : ""); + + String resp = HttpUtil.doPost(constants.QUERY_PUSH_INFO, postJsonObject.toString() , null); + System.out.println("queryByDeviceId result:" + resp); + if (StringUtils.isEmpty(resp)) { + return; + } + + JSONObject respObject = JSONObject.parseObject(resp); + + commonOpt.handleTargetUrl(respObject, keys); + + handleEmailAndRiod(keys, respObject); + } + } + + + private void handleEmailAndRiod(Map keys, JSONObject alertForwardConfig) { + if (keys.containsKey("status")) { + String deviceId = keys.get("deviceId").toString(); + String redisStatus = (String) hashOperations.get(REDIS_DEVICE_ALARM_KEY, deviceId); + System.out.println("redis device alarm status: " + redisStatus); + + try { + String status = keys.get("status").toString(); + + if (redisStatus == null || redisStatus.isEmpty()) { + RedisAlarmDTO redisAlarmDTO = new RedisAlarmDTO(); + int type = -1; + if (STATUS_ALARM.equalsIgnoreCase(status)) { + type = 1; + // roid + roid(keys, type, redisAlarmDTO, deviceId, alertForwardConfig); + } else if (STATUS_ALERT_CANCEL.equalsIgnoreCase(status)) { + type = 2; + } + redisAlarmDTO.setStatus(status); + hashOperations.put(REDIS_DEVICE_ALARM_KEY, deviceId, new ObjectMapper().writeValueAsString(redisAlarmDTO)); + // email + switchAlarmSendMail(keys, type, deviceId, alertForwardConfig); + } else { + ObjectMapper objectMapper = new ObjectMapper(); + RedisAlarmDTO redisAlarmDTO = objectMapper.readValue(redisStatus, RedisAlarmDTO.class); + System.out.println("redis data already exists, new status: " + status + ", redis status: " + redisAlarmDTO.getStatus()); + int type = -1; + if (STATUS_ALARM.equalsIgnoreCase(status) && !STATUS_ALARM.equalsIgnoreCase(redisAlarmDTO.getStatus())) { + // 告警 + type = 1; + } else if (STATUS_ALERT_CANCEL.equalsIgnoreCase(status) && STATUS_ALARM.equalsIgnoreCase(redisAlarmDTO.getStatus())) { + // 恢复告警 + type = 2; + } + roid(keys, type, redisAlarmDTO, deviceId, alertForwardConfig); + redisAlarmDTO.setStatus(status); + hashOperations.put(REDIS_DEVICE_ALARM_KEY, deviceId, new ObjectMapper().writeValueAsString(redisAlarmDTO)); + // email + switchAlarmSendMail(keys, type, deviceId, alertForwardConfig); + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println("handleEmailAndRiod error : " + e.getMessage()); + } + } + } + + + private void switchAlarmSendMail(Map keys, int type, String deviceId, JSONObject alertForwardConfig) { + if (!keys.containsKey("forwardType") || + (!keys.get("forwardType").toString().contains("mail") && !keys.get("forwardType").toString().contains("MAIL"))) { + return; + } + String parseAlertContent = alertForwardConfig.getJSONObject("data").getString("alertContent"); + + String subject = getEmailSubject(type, keys, alertForwardConfig); + + String emails =constants.ALARM_RECEIVER; + + commonOpt.switchAlarmSendMail(type, emails, parseAlertContent, subject, deviceId); + + } + + + private String getEmailSubject(int type, Map keys, JSONObject alertForwardConfig) { + if (!StringUtils.isEmpty(alertForwardConfig.getJSONObject("data").getString("alertTitle"))) { + return alertForwardConfig.getJSONObject("data").getString("alertTitle"); + } else { + String alarmType = alertForwardConfig.getJSONObject("data").getJSONObject("deviceAlertConfig").getString("type"); + + return commonOpt.getEmailSubject(alarmType, type, keys, alertForwardConfig); + } + } + + + /** + * + * @param keys + * @param type 1-告警,2-恢复告警 + * @param deviceId + * @param redisAlarmDTO + */ + private void roid(Map keys, int type, RedisAlarmDTO redisAlarmDTO, String deviceId, JSONObject alertForwardConfig) { + if (!keys.containsKey("forwardType") || + (!keys.get("forwardType").toString().contains("api") && !keys.get("forwardType").toString().contains("API"))) { + return; + } + String parseAlertContent = alertForwardConfig.getJSONObject("data").getString("alertContent"); + + String emailSubject = getEmailSubject(2, keys, alertForwardConfig); + + commonOpt.roid(parseAlertContent, emailSubject, keys, type, redisAlarmDTO); + + } +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/service/AlarmDataPushService.java b/src/main/java/com/techsor/datacenter/sender/service/AlarmDataPushService.java new file mode 100644 index 0000000..9b11b04 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/AlarmDataPushService.java @@ -0,0 +1,684 @@ +package com.techsor.datacenter.sender.service; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.hash.Hash; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.techsor.datacenter.sender.components.SendMail; +import com.techsor.datacenter.sender.dao.DeviceAlertConfigDao; +import com.techsor.datacenter.sender.dao.DeviceAlertTemplateBindDao; +import com.techsor.datacenter.sender.dao.DeviceAlertTemplateDao; +import com.techsor.datacenter.sender.dao.DeviceDao; +import com.techsor.datacenter.sender.dto.DeviceAlertInfo; +import com.techsor.datacenter.sender.dto.RedisAlarmDTO; +import com.techsor.datacenter.sender.dto.VariableBoundsDTO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.utils.DataUtils; +import com.techsor.datacenter.sender.utils.HttpUtil; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import jakarta.annotation.Resource; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class AlarmDataPushService { + + @Resource + private SendMail sendMail; + private static final String REDIS_DEVICE_ALARM_KEY = "lambda_device_alarm"; + + private static final String ALERT_ALARM_REDIS_PREFIX="alert:alert_alert"; + private static final String STATUS_ALARM = "alert"; + private static final String STATUS_ALERT_CANCEL = "alert_cancel"; + + private static final String STATUS_NORMAL="normal"; + private static final String ALERT_CANCEL_INFO = "【%s アラートメール】%s %s の%s解除を検知しました"; + private static final String ALERT_INFO = "【%s アラートメール】%s %s で%sしました"; + + private static final String TMPL_VALUE_REGEX = "\\{(.*?)\\}"; + @Resource + RestTemplate httpRestTemplate; + + @Value("${query.push.info}") + private String queryPushInfo; + @Value("${business.query.push.info}") + private String businessQueryPushInfo; + @Resource + RedisTemplate alramRedisTemplate; + + + @Value("${roid.alarm.url}") + private String roidAlarmUrl; + + + @Value("${roid.alarm.cancel.url}") + private String roidAlarmCancelUrl; + + + @Value("${roid.authorization}") + private String roidAuthorization; + + + @Value("${alarm.receiver}") + private String alarmReceiver; + + + @Value("${roid2.url}") + private String roid2Url; + + @Value("${roid2.authorization}") + private String roid2Authorization; + + + @Resource + private DeviceDao deviceDao; + + + + @Resource + private DeviceAlertTemplateBindDao deviceAlertTemplateBindDao; + + + @Resource + private DeviceAlertTemplateDao deviceAlertTemplateDao; + + @Resource + private DeviceAlertConfigDao deviceAlertConfigDao; + /** + * 分发警报记录。根据设备ID和原始JSON数据,分析警报内容并进行相应的处理。 + * 如果原始JSON中包含需要传递的警报内容,则构造一个新的JSON对象,并根据警报状态添加相应的警报内容。 + * 然后,将这个新的JSON对象通过HTTP POST请求发送到一个指定的URL。 + * 如果响应为空,则不进行任何操作。 + * 如果存在需要转发的目标,则会根据响应数据中提供的URL列表进行数据转发。 + * 最后,调用handleEmailAndRoid方法处理邮件和Roid通知。 + * + * @param deviceId 设备ID + * @param rawJson 原始JSON字符串 + * @param filterTargetSets 过滤目标集合 + * @param alertTriggerExpression 告警表达式 + * @throws Exception 可能抛出的异常 + */ + /** + * 分发警报记录。根据设备ID和原始JSON数据,分析警报内容并进行相应的处理。 + * 如果原始JSON中包含需要传递的警报内容,则构造一个新的JSON对象,并根据警报状态添加相应的警报内容。 + * 然后,将这个新的JSON对象通过HTTP POST请求发送到一个指定的URL。 + * 如果响应为空,则不进行任何操作。 + * 如果存在需要转发的目标,则会根据响应数据中提供的URL列表进行数据转发。 + * 最后,调用handleEmailAndRoid方法处理邮件和Roid通知。 + * + * @param deviceId 设备ID + * @param rawJson 原始JSON字符串 + * @param filterTargetSets 过滤目标集合 + * @param alertTriggerExpression 告警表达式 + * @param type 类型 + * @throws Exception 可能抛出的异常 + */ + public void dispatchAlarmRecord(String deviceId, String rawJson, Set filterTargetSets, + String alertTriggerExpression, int type) throws JsonProcessingException { + JSONObject currentRawJsonObject = JSON.parseObject(rawJson); + + if (StringUtils.isNotEmpty(currentRawJsonObject.getString("rawData"))) { + List deviceEntityList = this.deviceDao.getDeviceInfoByDeviceId(deviceId); + if (CollectionUtil.isEmpty(deviceEntityList)) { + log.error("deviceEntityList is null=======> {}", deviceId); + return; + } + + DeviceEntity currentDevice = deviceEntityList.get(0); + Integer deviceConfigId = currentDevice.getId(); + + List currentDeviceAlertInfoBindTemplateLists = this.deviceAlertTemplateDao.selectDeviceAlertTemplatesByDeviceId(Long.valueOf(deviceConfigId)); + if (CollectionUtil.isEmpty(currentDeviceAlertInfoBindTemplateLists)) { + log.error("currentDeviceAlertInfoBindTemplateLists is null=======> {}", deviceId); + return; + } + boolean firstRoundCheck = false; + for (int i = 0; i < currentDeviceAlertInfoBindTemplateLists.size(); i++) { + DeviceAlertInfo deviceAlertInfo = currentDeviceAlertInfoBindTemplateLists.get(i); + JSONObject postJsonObject = createPostJsonObject(deviceId, currentRawJsonObject, deviceAlertInfo); + + + String resp = HttpUtil.doPost(queryPushInfo, postJsonObject.toString(), null); + log.info("queryByDeviceId result:{}", resp); + + if (StringUtils.isEmpty(resp)) { + continue; + } + + JSONObject respObject = JSONObject.parseObject(resp); +// //Process Value replace/用于替换模版中的{Value}字段 +// JSONArray parsedAlarmInfoListArray = respObject.getJSONObject("data").getJSONArray("parsedAlarmInfoList"); +// JSONObject jsonObject = JSON.parseObject(currentRawJsonObject.getString("rawData")); +// List valueList = new ArrayList<>(); +// for (String key : jsonObject.keySet()) { +// valueList.add(jsonObject.get(key)); +// } +// String pureValue = StringUtils.join(valueList, ","); +// for (int k = 0; k < parsedAlarmInfoListArray.size(); k++) { +// // 确保元素是 JSONObject 类型(避免 ClassCastException)[[7]] +// // 替换每个数组对象中的alertContent中的Value变量为实际值 +// if (parsedAlarmInfoListArray.get(k) instanceof JSONObject) { +// JSONObject alarmInfo = parsedAlarmInfoListArray.getJSONObject(k); +// String alertContent = alarmInfo.getString("alertContent"); +// alarmInfo.put("alertContent",alertContent.replace("{Value}",pureValue)); +// parsedAlarmInfoListArray.set(k,alarmInfo); +// } +// } +// log.info("parsedAlarmInfoListArray:{}", parsedAlarmInfoListArray); + + if (currentRawJsonObject.containsKey("needTransfer") && "true".equalsIgnoreCase(currentRawJsonObject.getString("needTransfer"))) { + transferAlarm(currentRawJsonObject, respObject); + } + if (i == 0) { + firstRoundCheck = handleEmailAndRoidFirstRound(currentRawJsonObject, respObject, deviceAlertInfo.getForwardType(), filterTargetSets, alertTriggerExpression); + } else { + handleEmailAndRoidSubsequentRounds(currentRawJsonObject, respObject, deviceAlertInfo.getForwardType(), filterTargetSets, alertTriggerExpression, firstRoundCheck); + } + } + } + } + + /** + * 处理第一轮的邮件和Roid通知。 + * + * @param keys JSON对象,包含警报信息和配置 + * @param alertForwardConfig 警报转发配置的JSON对象 + * @param forwardType 转发类型 + * @param filterTargetSets 过滤目标集合 + * @param alertTriggerExpression 警报触发表达式 + * @return 如果处理了警报则返回true,否则返回false + * @throws JsonProcessingException 解析JSON时可能抛出的异常 + */ + private boolean handleEmailAndRoidFirstRound(JSONObject keys, JSONObject alertForwardConfig, String forwardType, Set filterTargetSets, String alertTriggerExpression) throws JsonProcessingException { + if (!keys.containsKey("status")) { + return false; + } + + String deviceId = keys.getString("deviceId"); + String status = keys.getString("status"); + int type = typeFromStatus(status); + + // 连接到Redis实例 + String redisStatus = (String) this.alramRedisTemplate.opsForHash().get(REDIS_DEVICE_ALARM_KEY, deviceId); + log.info("redis device alarm status: {}", redisStatus); + log.debug("current keys: {}", JSON.toJSONString(keys)); + log.debug("current JSONObject alertForwardConfig: {}", JSON.toJSONString(alertForwardConfig)); + + RedisAlarmDTO redisAlarmDTO = StringUtils.isEmpty(redisStatus) ? new RedisAlarmDTO() : new ObjectMapper().readValue(redisStatus, RedisAlarmDTO.class); + boolean shouldHandle = shouldHandleAlarm(status, redisAlarmDTO.getStatus()); + + if (shouldHandle) { + redisAlarmDTO.setStatus(status); + this.alramRedisTemplate.opsForHash().put(REDIS_DEVICE_ALARM_KEY, deviceId, JSONObject.toJSONString(redisAlarmDTO)); + + handleNotification(keys, type, deviceId, alertForwardConfig, forwardType, filterTargetSets, alertTriggerExpression); + + if (STATUS_ALERT_CANCEL.equalsIgnoreCase(status) || STATUS_NORMAL.equalsIgnoreCase(status)) { + String deleteRedisKey = ALERT_ALARM_REDIS_PREFIX + deviceId; + this.alramRedisTemplate.delete(deleteRedisKey); + } + + this.alramRedisTemplate.opsForHash().put(REDIS_DEVICE_ALARM_KEY, deviceId, JSONObject.toJSONString(redisAlarmDTO)); + return true; + } else { + log.warn("should not reach here {},{},{},{},{}", keys, alertForwardConfig, forwardType, filterTargetSets, alertTriggerExpression); + return false; + } + } + + /** + * 处理后续轮次的邮件和Roid通知。 + * + * @param keys JSON对象,包含警报信息和配置 + * @param alertForwardConfig 警报转发配置的JSON对象 + * @param forwardType 转发类型 + * @param filterTargetSets 过滤目标集合 + * @param alertTriggerExpression 警报触发表达式 + * @param firstRoundCheck 第一轮检查是否通过 + * @throws JsonProcessingException 解析JSON时可能抛出的异常 + */ + private void handleEmailAndRoidSubsequentRounds(JSONObject keys, JSONObject alertForwardConfig, String forwardType, Set filterTargetSets, String alertTriggerExpression, boolean firstRoundCheck) throws JsonProcessingException { + if (!keys.containsKey("status")) { + return; + } + + String deviceId = keys.getString("deviceId"); + String status = keys.getString("status"); + int type = typeFromStatus(status); + + if (firstRoundCheck) { + handleNotification(keys, type, deviceId, alertForwardConfig, forwardType, filterTargetSets, alertTriggerExpression); + } + } + + private JSONObject createPostJsonObject(String deviceId, JSONObject currentRawJsonObject, DeviceAlertInfo deviceAlertInfo) { + JSONObject postJsonObject = new JSONObject(); + postJsonObject.put("deviceId", deviceId); + String status = currentRawJsonObject.getString("status"); + + if (STATUS_ALARM.equalsIgnoreCase(status)) { + postJsonObject.put("alertContent", deviceAlertInfo.getTemplateDefs()); + postJsonObject.put("alertTitle", deviceAlertInfo.getAlertTitle()); + } else if (STATUS_ALERT_CANCEL.equalsIgnoreCase(status)) { + postJsonObject.put("alertContent", deviceAlertInfo.getAlertCancelMessage()); + postJsonObject.put("alertTitle", deviceAlertInfo.getAlertCancelTitle()); + } else { + postJsonObject.put("alertContent", deviceAlertInfo.getTemplateDefs()); + postJsonObject.put("alertTitle", deviceAlertInfo.getAlertTitle()); + } + return postJsonObject; + } + + private void transferAlarm(JSONObject currentRawJsonObject, JSONObject respObject) { + String targetData = currentRawJsonObject.getString("rawData"); + List targetUrlList = new ArrayList<>(); + JSONArray dataArr = respObject.getJSONObject("data").getJSONArray("dataList"); + + if (respObject.getIntValue("code") == 200 && dataArr != null) { + for (Object data : dataArr) { + String targetForwardParamsStr = JSONObject.parseObject(data.toString()).getString("targetForwardParams"); + if (StringUtils.isNotEmpty(targetForwardParamsStr)) { + JSONObject tfpObject = JSONObject.parseObject(targetForwardParamsStr); + targetUrlList.add(tfpObject.getString("url")); + } + } + } + + if (!targetUrlList.isEmpty()) { + for (String targetUrl : targetUrlList) { + if (StringUtils.isNotEmpty(targetUrl)) { + String pushResp = HttpUtil.doPost(targetUrl, targetData, null); + log.info("push alarm result:{}", pushResp); + } + } + } else { + log.info("alarm push url is null........."); + } + } + + private boolean shouldHandleAlarm(String newStatus, String existingStatus) { + return (STATUS_ALARM.equalsIgnoreCase(newStatus) && !STATUS_ALARM.equalsIgnoreCase(existingStatus)) || + (STATUS_ALERT_CANCEL.equalsIgnoreCase(newStatus) && STATUS_ALARM.equalsIgnoreCase(existingStatus)) || + (STATUS_NORMAL.equalsIgnoreCase(newStatus) && STATUS_ALERT_CANCEL.equalsIgnoreCase(existingStatus)); + } + + private int typeFromStatus(String status) { + if (STATUS_ALARM.equalsIgnoreCase(status)) { + return 1; + } else if (STATUS_ALERT_CANCEL.equalsIgnoreCase(status)) { + return 2; + } + return -1; + } + + private void handleNotification(JSONObject keys, int type, String deviceId, JSONObject alertForwardConfig, String forwardType, Set filterTargetSets, String alertTriggerExpression) { + switch (forwardType.toLowerCase()) { + case "roid1": + handleRoidFirst(keys, type, new RedisAlarmDTO(), deviceId, alertForwardConfig, filterTargetSets); + break; + case "roid2": + handleRoidSecond(keys, alertTriggerExpression, filterTargetSets, type); + break; + case "mail": + switchAlarmSendMail(keys, type, deviceId, alertForwardConfig); + break; + default: + log.warn("Unsupported forward type: {}", forwardType); + } + } + + + /** + * 根据警报类型和配置发送邮件。如果警报配置指定了邮件转发,将根据警报类型和内容构造邮件主题和正文,并发送邮件。 + * + * @param keys JSON对象,包含警报信息和配置 + * @param type 警报类型,1表示新警报,2表示取消警报 + * @param deviceId 设备ID + * @param alertForwardConfig 警报转发配置的JSON对象 + */ + + private void switchAlarmSendMail(JSONObject keys, int type, String deviceId, JSONObject alertForwardConfig) { + log.debug("switchAlarmSendMail {} {} {}",keys,deviceId,alertForwardConfig); + + String defaultMemo = ""; + if (2 == type) { + defaultMemo = "設備が正常に戻りました"; + } + String parseAlertContent=""; + if (Objects.nonNull(alertForwardConfig) && alertForwardConfig.containsKey("data") && alertForwardConfig.getJSONObject("data").containsKey("alertContent")){ + parseAlertContent = alertForwardConfig.getJSONObject("data").getString("alertContent"); + } + parseAlertContent = replaceValue(keys, parseAlertContent); + String sendHtml = getMemo(parseAlertContent, defaultMemo); + + log.debug("current DeviceInfo {}",keys); + JSONObject resultObject = queryAlertForwardConfigByAlarmTmplIds(keys); + log.debug("current business query push info {}",resultObject); + if (Objects.nonNull(resultObject)){ + log.debug("begin to process resultObject {}",resultObject); + JSONArray listArray = resultObject.getJSONObject("data").getJSONArray("parsedAlarmInfoList"); + log.debug("current listArray {}",listArray); + if (null != listArray && listArray.size() > 0) { + for (Object object : listArray) { + log.debug("current Object:{}",object); + JSONObject respObject = JSONObject.parseObject(object.toString()); + log.debug("current respObject:{}",object); + if (!respObject.containsKey("forwardType") || + (!respObject.getString("forwardType").contains("mail") && !respObject.getString("forwardType").contains("MAIL"))) { + continue; + } + + String emails = respObject.getString("alertRecipientMail"); + log.debug("current emails:{}",emails); + if (StringUtils.isEmpty(emails)) { + continue; + } + List emailList = Arrays.asList(emails.contains(",") ? StringUtils.split(emails, ",") : new String[]{emails}); + List sendList = new ArrayList(emailList); + //收件人 + String receiver = sendList.get(0); + + + String alarmType = null; // Initialize the alarm type as null + + try { + JSONObject data = alertForwardConfig.getJSONObject("data"); + JSONObject deviceAlertConfig = (data == null) ? null : data.getJSONObject("deviceAlertConfig"); + alarmType = (deviceAlertConfig == null) ? "" : deviceAlertConfig.getString("type"); + } catch (Exception e) { // Catch JSON parsing exceptions + log.error("JSON parsing error: {}" , e.getMessage(),e); + } + + + String subject = getEmailSubject(alarmType,type, keys, respObject); + String cc = org.springframework.util.StringUtils.collectionToDelimitedString(sendList, ","); + log.debug("begin to send email"); + this.sendMail.sendMail(subject, sendHtml, receiver, cc); + log.debug("end send email"); + } + } + } + + } + private String replaceValue(Map keys, String alertContent) { + if (keys.containsKey("rawData") && null != keys.get("rawData")) { + String rawData = (String) keys.get("rawData"); + + JSONObject rawDataObject = JSONObject.parseObject(rawData); + + Pattern pattern = Pattern.compile(TMPL_VALUE_REGEX); + Matcher matcher = pattern.matcher(alertContent); + while (matcher.find()) { + String match = matcher.group(1); // 获取匹配到的内容 + for (String key : rawDataObject.keySet()) { + if (key.endsWith(match)) { + alertContent = alertContent.replace("{" + match + "}", rawDataObject.getString(key)); + } + } + } + } + return alertContent; + } + + + /** + * 根据警报类型和配置生成邮件主题。如果警报配置中包含标题,则使用该标题,否则根据设备信息和警报类型构造默认标题。 + * + + * @return 生成的邮件主题字符串 + */ + private String getJsonString(JSONObject keys, String key) { + if (keys.containsKey(key) && keys.get(key) != null) { + try { + JSONObject obj = JSONObject.parseObject(keys.get(key).toString()); + if (obj != null) { + return obj.getString("name"); // Using getString with a default value + } + } catch (Exception e) { + log.error("Error parsing JSON for key '" + key + "': " + e.getMessage()); + } + } + return ""; + } + + private String getEmailSubject(String alarmType, int type, JSONObject keys, JSONObject alertForwardConfig) { + return Optional.ofNullable(alertForwardConfig) + .map(config -> config.getString("alertTitle")) + .filter(title -> !title.isEmpty()) + .orElseGet(() -> { + String buildingName = getJsonString(keys, "buildingInfo"); + String floorName = getJsonString(keys, "floorInfo"); + String spaceName = getJsonString(keys, "spaceInfo"); + + switch (type) { + case 1: + return String.format(ALERT_INFO, buildingName, floorName, spaceName, alarmType); + case 2: + return String.format(ALERT_CANCEL_INFO, buildingName, floorName, spaceName, alarmType); + default: + return ""; + } + }); + } + /** + * 生成邮件正文内容。如果指定了警报内容,则使用该内容,否则使用默认备忘录。 + * + * @param parseAlertContent 解析后的警报内容 + * @param defaultMemo 默认备忘录内容 + * @return 生成的邮件正文内容 + */ + + private String getMemo(String parseAlertContent, String defaultMemo) { + return StringUtils.isEmpty(parseAlertContent) ? defaultMemo : parseAlertContent; + } + + + /** + * 发送Roid通知。根据警报类型和目标ID发送Roid通知,并更新Redis中的警报状态。 + * + * @param keys JSON对象,包含警报信息和配置 + * @param type 警报类型,1表示新警报,2表示取消警报 + * @param redisAlarmDTO Redis警报数据传输对象 + * @param deviceId 设备ID + * @param alertForwardConfig 警报转发配置的JSON对象 + * @param filterTargetSets 过滤目标集合 + */ + private void handleRoidFirst(JSONObject keys, int type, RedisAlarmDTO redisAlarmDTO, String deviceId, + JSONObject alertForwardConfig, Set filterTargetSets) { + String targetIdList = keys.getString("targetId"); + filterTargetSets = filterTargetSets.stream() + .map(iter -> iter.replaceAll("[\\[\\]'\" ]", "")) + .collect(Collectors.toSet()); + log.info("keys:{},targetIdList:{}", keys, targetIdList); + + if (StringUtils.isEmpty(targetIdList)) { + log.debug("targetIdList is empty"); + return; + } + + Map paramsMap = JSON.parseObject(targetIdList, new TypeReference>() {}); + if (paramsMap.isEmpty()) { + return; + } + Set finalFilterTargetSets = filterTargetSets; + paramsMap.forEach((iterKey, standardTargetId) -> { + iterKey = iterKey.replaceAll("[\\[\\]'\" ]", ""); + keys.put("targetId", standardTargetId); + if (finalFilterTargetSets.contains(iterKey)) { + roid1(keys, type, redisAlarmDTO, deviceId, alertForwardConfig); + } + }); + } + + /** + * 发送Roid通知。根据警报类型和目标ID发送Roid通知,并更新Redis中的警报状态。 + * + * @param keys JSON对象,包含警报信息和配置 + * @param alertExpression 警报表达式 + * @param type 警报类型,1表示新警报,2表示取消警报 + * @param filterTargetSets 过滤目标集合 + */ + private void handleRoidSecond(JSONObject keys, String alertExpression, Set filterTargetSets, int type) { + + filterTargetSets = filterTargetSets.stream() + .map(iter -> iter.replaceAll("[\\[\\]'\" ]", "")) + .collect(Collectors.toSet()); + + String targetIdList = keys.getString("targetId"); + if (StringUtils.isEmpty(targetIdList)) { + return; + } + + Map paramsMap = JSON.parseObject(targetIdList, new TypeReference>() {}); + if (paramsMap.isEmpty()) { + return; + } + + Map rawDataMap = JSON.parseObject(keys.getString("rawData"), new TypeReference>() {}); + VariableBoundsDTO variableBoundsDTO = DataUtils.extractMinMax(alertExpression); + + Set finalFilterTargetSets = filterTargetSets; + paramsMap.forEach((iterKey, standardTargetId) -> { + iterKey = iterKey.replaceAll("[\\[\\]'\" ]", ""); + Object targetIdValue = MapUtils.getObject(rawDataMap, iterKey); + + if (finalFilterTargetSets.contains(iterKey)) { + keys.put("forwardValue", targetIdValue); + keys.put("targetId", standardTargetId); + handleRoid2Data(keys, type, standardTargetId, rawDataMap, variableBoundsDTO); + } + }); + } + + private void roid1(JSONObject keys, int type, RedisAlarmDTO redisAlarmDTO, String deviceId, JSONObject alertForwardConfig) { + + String parseAlertContent = alertForwardConfig.getJSONObject("data").getString("alertContent"); + + HashMap headerMap = new HashMap<>(); + headerMap.put("Content-Type", "application/json;charset=UTF-8"); + headerMap.put("access-control-allow-credentials", "True"); + headerMap.put("X-Requested-With", "XMLHttpRequest"); + headerMap.put("Authorization", roidAuthorization); + headerMap.put("Accept", "application/json, text/plain, /"); + + if (type == 1) { + handleNewAlarm(keys, parseAlertContent, headerMap, redisAlarmDTO); + } else if (type == 2 && redisAlarmDTO != null && redisAlarmDTO.getProblemReportId() != null) { + handleCancelAlarm(keys, parseAlertContent, headerMap, redisAlarmDTO, alertForwardConfig); + } + } + + private void handleNewAlarm(JSONObject keys, String parseAlertContent, HashMap headerMap, RedisAlarmDTO redisAlarmDTO) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("problem_report_category_id", keys.getLong("problemReportCategoryId")); + jsonObject.put("target_id", Long.valueOf(keys.getString("targetId"))); + jsonObject.put("memo", getMemo(parseAlertContent, "").replace("
    ", " ")); + + if (keys.containsKey("buildingId")) { + jsonObject.put("building_id", keys.getLong("buildingId")); + } + + String resp = HttpUtil.doPost(roidAlarmUrl, jsonObject.toString(), headerMap); + log.info("roid alarm api resp : {}", resp); + + if (StringUtils.isNotEmpty(resp)) { + JSONObject respObject = JSONObject.parseObject(resp); + if (respObject.containsKey("id")) { + redisAlarmDTO.setProblemReportId(respObject.getLongValue("id")); + } + } + } + + private void handleCancelAlarm(JSONObject keys, String parseAlertContent, HashMap headerMap, RedisAlarmDTO redisAlarmDTO, JSONObject alertForwardConfig) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("problem_report_id", redisAlarmDTO.getProblemReportId()); + jsonObject.put("memo", getMemo(parseAlertContent, getEmailSubject("", 2, keys, alertForwardConfig)).replace("
    ", " ")); + + String resp = HttpUtil.doPost(roidAlarmCancelUrl, jsonObject.toString(), headerMap); + log.info("roid cancel alarm api resp : {}", resp); + } + + + + public void handleRoid2Data(JSONObject keys, int type, Object targetId, Map rawDataMap, VariableBoundsDTO variableBoundsDTO) { + if (!keys.containsKey("forwardTypeList") || Objects.isNull(keys.getJSONArray("forwardTypeList"))) { + return; + } + + List forwardTypeList = keys.getJSONArray("forwardTypeList").toJavaList(String.class); + + if (forwardTypeList.stream().noneMatch(ft -> ft.equalsIgnoreCase("roid2"))) { + return; + } + + String replaceRoid2Url = org.apache.commons.lang3.StringUtils.replace(roid2Url, "{targetId}", String.valueOf(targetId)); + HashMap headerMap = new HashMap<>(); + headerMap.put("Content-Type", "application/json;charset=UTF-8"); + headerMap.put("access-control-allow-credentials", "True"); + headerMap.put("X-Requested-With", "XMLHttpRequest"); + headerMap.put("Authorization", roid2Authorization); + headerMap.put("Accept", "application/json, text/plain, /"); + + if (2 == type) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("status", 0); + jsonObject.put("value", keys.get("forwardValue")); + String formattedUpperBound = variableBoundsDTO.getUpperBound() == null ? null : String.format("%.5f", variableBoundsDTO.getUpperBound()); + String formattedLowerBound = variableBoundsDTO.getLowerBound() == null ? null : String.format("%.5f", variableBoundsDTO.getLowerBound()); + + jsonObject.put("normal_upper",formattedUpperBound); + jsonObject.put("normal_lower",formattedLowerBound); + log.info("roid2 alarm api req : {}", JSON.toJSONString(jsonObject)); + String resp = HttpUtil.doPost(replaceRoid2Url, jsonObject.toString(), headerMap); + log.info("roid2 alarm api resp : {}", resp); + } else if (1 == type) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("status", 1); + jsonObject.put("value",keys.get("forwardValue")); + jsonObject.put("normal_upper",variableBoundsDTO.getUpperBound()); + jsonObject.put("normal_lower",variableBoundsDTO.getLowerBound()); + log.info("roid2 alarm api req : {}", JSON.toJSONString(jsonObject)); + String resp = HttpUtil.doPost(replaceRoid2Url, jsonObject.toString(), headerMap); + log.info("roid2 cancel alarm api resp : {}", resp); + } + } + + private JSONObject queryAlertForwardConfigByAlarmTmplIds(JSONObject keys) { + if (keys == null || keys.isEmpty()) { + return null; + } + + String alertTemplateIds = keys.getString("alertTemplateIds"); + String rawData = keys.getString("rawData"); + + if (StringUtils.isEmpty(alertTemplateIds) || StringUtils.isEmpty(rawData)) { + return null; + } + + JSONObject postJsonObject = new JSONObject(); + postJsonObject.put("deviceId", keys.getString("deviceId")); + postJsonObject.put("status", keys.getString("status")); + postJsonObject.put("alarmTmplIds", alertTemplateIds.replace("[", "").replace("]", "")); + + String resp = HttpUtil.doPost(businessQueryPushInfo, postJsonObject.toString(), null); + log.info("queryByDeviceId queryAlertForwardConfigByAlarmTmplIds result: {}", resp); + + return JSONObject.parseObject(resp); + } + +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/service/DeltaInnerService.java b/src/main/java/com/techsor/datacenter/sender/service/DeltaInnerService.java new file mode 100644 index 0000000..346d635 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/DeltaInnerService.java @@ -0,0 +1,174 @@ +package com.techsor.datacenter.sender.service; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.techsor.datacenter.sender.dao.DeviceDao; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.delta.DeltaCacheEntity; +import com.techsor.datacenter.sender.entitiy.delta.DeltaNumEntity; + +import cn.hutool.core.collection.CollectionUtil; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.lang.reflect.Type; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Delta内部服务类,用于处理Delta设备发来的数据。 + *

    + * 此服务类负责接收和解析Delta设备的数据,将其归类到对应的客户端缓存中,并根据数据类型生成相应的数据消息。 + */ +@Service("deltaInnerService") +public class DeltaInnerService { + + + @Resource + private DeviceDao deviceDao; + + private final static Logger myLogger = LoggerFactory.getLogger(DeltaInnerService.class); + /** + * 启动数据处理流程。 + * @param content JSON格式的字符串数据。 + * @return 处理后的第一个结果字符串,如果结果列表为空则返回空字符串。 + */ + public String start(String content) { + myLogger.debug("DeltaInnerService start process json data :{}", content); + + // 解析数据 + Type listType = new TypeToken>() {}.getType(); + List dataList = new Gson().fromJson(content, listType); + + if (CollectionUtil.isEmpty(dataList)) { + return ""; + } + + // 数据归类 + List resultDelatCacheList=categorizedData(dataList); + + // 处理并发送数据 + List resultList = processAndSendData(resultDelatCacheList); + + return CollectionUtil.isEmpty(resultList) ? "" : resultList.get(0); + } + /** + * 处理并发送数据。 + * @param resultDelatCacheList 分类后的DeltaCacheEntity列表。 + * @return 生成的结果字符串列表。 + */ + + private List processAndSendData(List resultDelatCacheList) { + List resultList = new ArrayList<>(); + + String result=resultDelatCacheList.stream().map(cacheItem -> generateJGCode(cacheItem, cacheItem.getTypeId())).collect(Collectors.joining(",", "{", "}")); + if (result.endsWith(",}")) { + result = result.substring(0, result.length() - 2) + "}"; + } + if (!StringUtils.equals(result,"{}")){ + resultList.add(result); + } + + return resultList; + } + /** + * 对输入的DeltaNumEntity列表数据进行归类。 + * @param dataList 输入的DeltaNumEntity列表。 + * @return 归类后的DeltaCacheEntity列表。 + */ + private List categorizedData(List dataList){ + return dataList.stream().map(receivedItem -> { + String deviceId = receivedItem.getId(); + List deviceInfoList = this.deviceDao.getDeviceInfoByDeviceId(deviceId); + if (CollectionUtil.isEmpty(deviceInfoList)) { + return null; + } + DeviceEntity deviceInfoItem = deviceInfoList.get(0); + DeltaCacheEntity deviceTemp = new DeltaCacheEntity(); + deviceTemp.setDeviceId(deviceInfoItem.getDeviceId()); + if (Objects.isNull(deviceInfoItem.getDeviceSN())||StringUtils.isEmpty(deviceInfoItem.getDeviceSN())){ + deviceTemp.setDeviceSN(deviceInfoItem.getDeviceId()); + }else{ + deviceTemp.setDeviceSN(deviceInfoItem.getDeviceSN()); + } + deviceTemp.setTypeId(deviceInfoItem.getTypeId()); + deviceTemp.setValue(receivedItem.getValue()); + return deviceTemp; + }).collect(Collectors.toList()); + } + + + + + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#0.00"); + + /** + * 根据DeltaCacheEntity实例和类型ID生成JGCode字符串。 + * @param item DeltaCacheEntity实例,包含设备信息。 + * @param typeId 类型ID,用于决定生成的键值对。 + * @return 生成的JGCode字符串,如果typeId不支持,则返回空字符串。 + * !!!!注意,Delta设备现在同时在KingIOServerService中进行处理!!! + */ + public String generateJGCode(DeltaCacheEntity item, Integer typeId){ + StringBuilder jgCodeBuilder = new StringBuilder("\"").append(item.getDeviceSN()).append("_"); + String tempKey; + switch (typeId){ + case 45: //Delta 测试 + tempKey = "malfunctioned_test"; + break; + case 46: //Delta 故障 + tempKey = "delta_failure"; + jgCodeBuilder.append(tempKey).append("\": ").append((Boolean) item.getValue() ? 1 : 0).append(","); + return jgCodeBuilder.toString(); + case 47: //Delta 计测 + tempKey = "delta_measurement"; + break; + case 48: //Delta 积算 + tempKey = "delta_accrual"; + break; + case 57: //Delta 故障 + tempKey = "delta_failure"; + jgCodeBuilder.append(tempKey).append("\": ").append((Boolean) item.getValue() ? 1 : 0).append(","); + return jgCodeBuilder.toString(); + case 58: //Delta 计测 + tempKey = "delta_measurement"; + break; + case 59: //Delta 积算 + tempKey = "delta_accrual"; + break; + case 52: //Delta 积算 0.1倍率 + tempKey = "delta_accrual"; + double tempValue = Double.parseDouble(item.getValue().toString()) * 0.1; + jgCodeBuilder.append(tempKey).append("\":").append(DECIMAL_FORMAT.format(tempValue)).append(","); + return jgCodeBuilder.toString(); + case 86: //ba-発停状態 + tempKey = "delta_status"; + jgCodeBuilder.append(tempKey).append("\":"); + if (item.getValue() instanceof Boolean) { + jgCodeBuilder.append((Boolean) item.getValue() ? 1 : 0); + } else if (item.getValue() instanceof Number) { // 处理数值类型 + jgCodeBuilder.append(item.getValue()); + } else { // 其他情况(字符串等) + jgCodeBuilder.append("\"").append(item.getValue()).append("\""); + } + jgCodeBuilder.append(","); + return jgCodeBuilder.toString(); + default: + myLogger.debug("Unsupported typeId: " + typeId); + return ""; + } + + // 适用于45、47、48条件的共通处理逻辑 + jgCodeBuilder.append(tempKey).append("\":").append(item.getValue().toString()).append(","); + myLogger.debug("GenerateMessage: " + jgCodeBuilder); + return jgCodeBuilder.toString(); + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/DeltaInnerServiceOld.java b/src/main/java/com/techsor/datacenter/sender/service/DeltaInnerServiceOld.java new file mode 100644 index 0000000..cc464d3 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/DeltaInnerServiceOld.java @@ -0,0 +1,181 @@ +package com.techsor.datacenter.sender.service; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.entitiy.DeltaClientCacheEntity; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.delta.DeltaCacheEntity; +import com.techsor.datacenter.sender.entitiy.delta.DeltaNumEntity; + +import cn.hutool.core.collection.CollectionUtil; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.lang.reflect.Type; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +/** + * Delta内部服务类,用于处理Delta设备发来的数据。 + *

    + * 此服务类负责接收和解析Delta设备的数据,将其归类到对应的客户端缓存中,并根据数据类型生成相应的数据消息。 + */ +@Service("deltaInnerServiceOld") +public class DeltaInnerServiceOld { + + + + @Resource + private CommonDAO commonDAO; + + + private final static Logger myLogger = LoggerFactory.getLogger(DeltaInnerServiceOld.class); + + + /** + * 开始处理Delta设备发来的数据。 + *

    + * 该方法首先解析传入的JSON字符串内容为DeltaNumEntity对象列表,然后根据设备ID归类数据到对应的客户端缓存中。 + * 最后,根据归类后的数据生成消息字符串,并返回。 + * + * @param content Delta设备发送的数据的JSON字符串 + * @return 处理后的数据消息字符串,如果无法处理则返回空字符串 + */ + public String start(String content){ + myLogger.debug("DeltaInnerService start process json data :{}",content); + //解析数据,数据格式为不带头的列表 + Type listType = new TypeToken>() {}.getType(); + List dataList = new Gson().fromJson(content, listType); + + //初始化clientId数组,因为一个clientId只能对应200个设备,这里一条数据可能存在属于多个clientId + List clientCacheList = new ArrayList<>(); + List deviceEntities; + //初始化ClientId + if (dataList.size()>0){ + //初始化Delta类型列表,目前只有46,47,48,52 + ArrayList typeIdList = new ArrayList<>(); + typeIdList.add(45); + typeIdList.add(46); + typeIdList.add(47); + typeIdList.add(48); + typeIdList.add(52); + //57、58、59 + typeIdList.add(57); + typeIdList.add(58); + typeIdList.add(59); + //获取所有属于Delta的设备信息 + deviceEntities = commonDAO.getDeviceInfoByTypeIds(typeIdList); + //初始化Delta绑定的所有clientId + List wsClientList = commonDAO.getWsClientIdListByTypeIds(typeIdList); + for (String item:wsClientList + ) { + DeltaClientCacheEntity cacheItem = new DeltaClientCacheEntity(); + cacheItem.setClientId(item); + ArrayList deviceItem = new ArrayList<>(); + cacheItem.setDeviceInfoList(deviceItem); + clientCacheList.add(cacheItem); + } + if (CollectionUtil.isEmpty(deviceEntities) || CollectionUtil.isEmpty(wsClientList)){ + return ""; + } + + }else{ + return ""; + } + //归类当前解析到的设备数据到clientCache列表中,完成后,clientCacheList中将clientId与设备列表对应关系 + for (DeltaNumEntity receivedItem:dataList){ + //遍历匹配收到的数据与数据库中读取的设备数据 + for (DeviceEntity deviceInfoItem:deviceEntities){ + if (deviceInfoItem.getDeviceId().equals(receivedItem.getId())){ + //匹配到设备数据后,将设备信息写入到对应的clientId缓存中 + for(DeltaClientCacheEntity cacheItem:clientCacheList){ + if (cacheItem.getClientId().equals(deviceInfoItem.getWsClientId())){ + DeltaCacheEntity deviceTemp = new DeltaCacheEntity(); + deviceTemp.setDeviceId(deviceInfoItem.getDeviceId()); + deviceTemp.setDeviceSN(deviceInfoItem.getDeviceSN()); + deviceTemp.setTypeId(deviceInfoItem.getTypeId()); + deviceTemp.setValue(receivedItem.getValue()); + cacheItem.getDeviceInfoList().add(deviceTemp); + } + } + } + } + } + + + List resultList=new ArrayList<>(); + //处理ClientCache缓存数据,按ClientId循环发送给DBM + for(DeltaClientCacheEntity cacheItem:clientCacheList){ + //拼接数据 + String tempWsClientId = cacheItem.getClientId(); + ArrayList tempDeviceList = cacheItem.getDeviceInfoList(); + String finalMsg = "{"; + for(DeltaCacheEntity item:tempDeviceList){ + String message = generateJGCode(item,item.getTypeId()); + finalMsg = finalMsg+message; + } + finalMsg = finalMsg.substring(0,finalMsg.length()-1)+"}"; + //发送数据到DBM + try{ + if (!finalMsg.equals("}")){ + resultList.add(finalMsg); + } + }catch (Exception e){ + myLogger.error(e.getMessage()); + } + } + if (CollectionUtil.isEmpty(resultList)){ + return ""; + } + return resultList.get(0); + } + /** + * 根据DeltaCacheEntity对象和设备类型ID生成相应的数据消息。 + *

    + * 此方法根据给定的参数生成特定格式的数据消息JSON字符串。 + * + * @param item DeltaCacheEntity对象,包含设备信息和数据值 + * @param typeId 设备类型ID + * @return 生成的数据消息JSON字符串 + */ + public String generateJGCode(DeltaCacheEntity item,Integer typeId){ + String JGCode="\""+item.getDeviceSN()+"_"; + String tempKey = ""; + switch (typeId){ + case 45: //Delta 测试 + tempKey="malfunctioned_test"; + JGCode = JGCode+tempKey+"\":"+item.getValue().toString()+","; + break; + case 46: //Delta 故障 + tempKey="delta_failure"; + if ((Boolean) item.getValue()==false){ + JGCode = JGCode+tempKey+"\": 0,"; + }else if ((Boolean) item.getValue()==true){ + JGCode = JGCode+tempKey+"\": 1,"; + } + break; + case 47: //Delta 計測 + tempKey="delta_measurement"; + JGCode = JGCode+tempKey+"\":"+item.getValue().toString()+","; + break; + case 48: //Delta 積算 + tempKey="delta_accrual"; + JGCode = JGCode+tempKey+"\":"+item.getValue().toString()+","; + break; + case 52: //Delta 積算 0.1倍率 + tempKey="delta_accrual"; + Double tempValue = Double.parseDouble(item.getValue().toString()) *0.1; + DecimalFormat df = new DecimalFormat("#0.00"); + JGCode = JGCode+tempKey+"\":"+df.format(tempValue)+","; + break; + } + + myLogger.debug("GenerateMessage: "+JGCode); + return JGCode; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/DispatchService.java b/src/main/java/com/techsor/datacenter/sender/service/DispatchService.java new file mode 100644 index 0000000..c96e261 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/DispatchService.java @@ -0,0 +1,53 @@ +package com.techsor.datacenter.sender.service; + + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import com.alibaba.fastjson2.JSONObject; +import com.techsor.datacenter.sender.dto.DeviceInfoVO; + +import jakarta.annotation.Resource; +import java.util.Set; + +@Slf4j +@Service +public class DispatchService { + + @Resource + private RestTemplate restTemplate; + + + + @Resource + AlarmDataPushService alarmDataPushService; + + @Resource + AlarmDataPushLambda alarmDataPushLambda; + + + /** + * 分发警报记录。根据设备ID和原始JSON数据,分析警报内容并进行相应的处理。 + * 如果原始JSON中包含需要传递的警报内容,则构造一个新的JSON对象,并根据警报状态添加相应的警报内容。 + * 然后,将这个新的JSON对象通过HTTP POST请求发送到一个指定的URL。 + * 如果响应为空,则不进行任何操作。 + * 如果存在需要转发的目标,则会根据响应数据中提供的URL列表进行数据转发。 + * 最后,调用handleEmailAndRoid方法处理邮件和Roid通知。 + * + * @param deviceId 设备ID + * @param alertTemplateInfo + * @param deviceInfoVO + */ + public void dispatchMessage(String companyId, String deviceId, String message,String alertTriggerExpression,Integer type,Set waitingProcessTargetSets, JSONObject alertTemplateInfo, DeviceInfoVO deviceInfoVO) { + try { + this.alarmDataPushLambda.dispatchAlarmRecord(companyId, deviceId, message,waitingProcessTargetSets,alertTriggerExpression,type, alertTemplateInfo, deviceInfoVO); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/IDataProcessService.java b/src/main/java/com/techsor/datacenter/sender/service/IDataProcessService.java new file mode 100644 index 0000000..bc0d37a --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/IDataProcessService.java @@ -0,0 +1,24 @@ +package com.techsor.datacenter.sender.service; + +import com.techsor.datacenter.sender.entitiy.DynamodbEntity; +import com.techsor.datacenter.sender.entitiy.ResultDeviceIdEntity; + +public interface IDataProcessService { + + String processData(String content, String dataSrcCode); + + String mockJsonData(String content, String dataSrcCode); + void save(String companyId, String content, String deviceId, String code,String receiveTsFromData); + + void saveMetcom(String content, String deviceId); + +// String getTopCompanyId(String deviceId); + + void processKingIOServerData(String processJson); + + void processSt150Data(String processJson); + + void processZAIoTData(ResultDeviceIdEntity entity); + + void minuteLevelStorage(DynamodbEntity baseTransDataEntity) throws Exception; +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/IDeviceTypeConfigService.java b/src/main/java/com/techsor/datacenter/sender/service/IDeviceTypeConfigService.java new file mode 100644 index 0000000..bc3df03 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/IDeviceTypeConfigService.java @@ -0,0 +1,25 @@ +package com.techsor.datacenter.sender.service; + + +import com.techsor.datacenter.sender.dao.DeviceConfigInfoDao; +import com.techsor.datacenter.sender.entitiy.DeviceConfigInfoEntity; +import com.techsor.datacenter.sender.exceptions.BusinessException; + +/** + * 定义设备访问接口 + * **/ +public interface IDeviceTypeConfigService { + + /*** + * 查询获取设备配置信息 + * */ + + public DeviceConfigInfoEntity getDeviceTypeConfig(String deviceId, Long deviceTypeId) throws BusinessException; + + + public DeviceConfigInfoEntity getDeviceTypeConfigByCode(String code) throws BusinessException; + + + public DeviceConfigInfoEntity getDeviceTypeConfigById(Long typeId) throws BusinessException; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/KingIOServerService.java b/src/main/java/com/techsor/datacenter/sender/service/KingIOServerService.java new file mode 100644 index 0000000..5e8b632 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/KingIOServerService.java @@ -0,0 +1,258 @@ +package com.techsor.datacenter.sender.service; + +import com.alibaba.fastjson2.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.techsor.datacenter.sender.components.CommonOpt; +import com.techsor.datacenter.sender.config.DataSourceContextHolder; +import com.techsor.datacenter.sender.dao.KingIOServerDAO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.kingio.KingIODataModel; +import com.techsor.datacenter.sender.entitiy.kingio.KingIODataItemEntity; +import com.techsor.datacenter.sender.entitiy.kingio.KingIODbmEntity; +import com.techsor.datacenter.sender.utils.TimeUtils; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 服务类,用于处理King IO Server发来的数据。 + *

    + * 此服务类负责接收和解析King IO Server的数据,将其转换为KingIODataModel对象,并根据数据处理逻辑生成相应的数据消息。 + */ +@Slf4j +@Component("kingIOServerService") +public class KingIOServerService { + + @Resource + KingIOServerDAO kingIOServerDAO; + @Autowired + private CommonOpt commonOpt; + + private final ObjectMapper objectMapper = new ObjectMapper(); + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#0.00"); + /** + * 将JSON字符串解析为KingIODataModel对象。 + * + * @param json King IO Server发送的数据的JSON字符串 + * @return 解析后的KingIODataModel对象 + * @throws IOException 当解析JSON时发生错误 + */ + public KingIODataModel parseJson(String json) throws IOException { + return objectMapper.readValue(json, KingIODataModel.class); + } + /** + * 开始处理King IO Server发来的数据。 + *

    + * 该方法首先调用parseJson方法解析数据,然后根据解析后的数据对象进行进一步处理。 + * + * @param data King IO Server发送的数据的JSON字符串 + * @return 处理后的数据消息,如果无法处理则返回空字符串 + * @throws Exception 当解析JSON或处理数据过程中发生错误 + */ + public String start(String data) throws Exception { + KingIODataModel dataModel = parseJson(data); + return processData(dataModel); + } + /** + * 根据解析后的KingIODataModel对象处理数据。 + *

    + * 此方法遍历数据对象中的数据项,根据数据项的类型和内容生成相应的数据消息字符串。 + * + * @param data 解析后的KingIODataModel对象 + * @return 生成的数据消息字符串,如果无法处理则返回空字符串 + * @throws ParseException 当处理数据时发生错误 + */ + public String processData(KingIODataModel data) throws ParseException { + List dataValues = new ArrayList<>(); + for (Map obj : data.getObjects()) { + String name = (String) obj.get("N"); + Object value = obj.getOrDefault("1", data.getPropertyValues().get("1")); + Object qualityStamp = obj.getOrDefault("3", data.getPropertyValues().get("3")); + String formattedTimestamp = (String) data.getPropertyValues().get("2"); + if (formattedTimestamp == null || formattedTimestamp.trim().isEmpty()) { + throw new IllegalArgumentException("formattedTimestamp is null or empty!"); + } + // 清理不可见字符并去除空格 + formattedTimestamp = formattedTimestamp.replaceAll("\\p{C}", "").trim(); + + try{ + // 检查质量戳 + if (!qualityStamp.equals(192) && !qualityStamp.equals(65535) && !qualityStamp.equals(0)) { + log.error("Error KingIOServer Data: 设备名 " + name + ", 质量戳: " + qualityStamp); + continue; + } + + try{ + // 处理时间戳 + if (obj.containsKey("2")) { + //以下注释是因为这里不要计算时间差,统一用数据头里的时间 + // long timestampOffset = Long.parseLong(obj.get("2").toString()); + Date date = parseDate(formattedTimestamp); + // date.setTime(date.getTime() + timestampOffset); + formattedTimestamp = dateFormat.format(date); + } + }catch (Exception e){ + log.error("Error processing timeproperties:"+(String) data.getPropertyValues().get("2")); + continue; + } + + + //Process the number value. keep 4 ecimal places + if (value instanceof Number) { + DecimalFormat df = new DecimalFormat("#.####"); // 保留四位小数的格式 + String formatted = df.format((Number)value); + value = Double.parseDouble(formatted); + }else if (value instanceof Boolean){ + if((Boolean)value==true){ + value = 1; + }else{ + value = 0; + } + + } + + // 输出数据解读 + log.debug("DeviceName:{}" , name); + log.debug("Value:{}" , value); + log.debug("Time:{}" , formattedTimestamp); + + dataValues.add(new KingIODataItemEntity(name, value, formattedTimestamp, String.valueOf(qualityStamp))); + }catch (Exception e){ + log.error("DeviceName:{}" , name); + log.error("Value:{}" , value); + log.error("Time:{}" , formattedTimestamp); + log.error("KINGIoserver error:",e); + } + + } + log.info("KingIOServer DataSize to process:{}",dataValues.size()); + List dbmValues = new ArrayList<>(); + for(KingIODataItemEntity dataItem : dataValues){ + String topCompanyId= commonOpt.getTopCompanyId(dataItem.getDeviceName()); + if (topCompanyId==null || topCompanyId.equals("0")){ + continue; + } + //Switch database + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + log.info("Use datasource for company:"+topCompanyId); + + KingIODbmEntity tempEntity = new KingIODbmEntity(); + DeviceEntity deviceItem = kingIOServerDAO.queryDeviceWsClientIdByDeviceId(dataItem.getDeviceName()); + if (deviceItem==null){ + continue; + } + log.info("KingIOServer process item:{},{},{}",dataItem.getDeviceName(),dataItem.getValue(),dataItem.getTimestamp()); + tempEntity.setDeviceId(deviceItem.getDeviceId()); + tempEntity.setContent(generateDBMStr(deviceItem,dataItem)); + try { + tempEntity.setTs(TimeUtils.kingiOServerTimeToTs(dataItem.getTimestamp())); + }catch (Exception e){ + log.error("Error KingIOServer Data: KingIODataItemEntity timestamp to utc timestamp error"); + log.error("Received Time : "+dataItem.getTimestamp()); + tempEntity.setTs(""); + } + dbmValues.add(tempEntity); + DataSourceContextHolder.clearCurrentDataSourceKey(); + } + + return JSON.toJSONString(dbmValues); + } + /** + * 根据设备实体和数据项生成DBM消息字符串。 + *

    + * 此方法根据设备的类型ID和数据项的内容生成特定格式的DBM消息字符串。 + * + * @param deviceItem 设备实体对象 + * @param dataItem 数据项实体对象 + * @return 生成的DBM消息字符串 + */ + private String generateDBMStr(DeviceEntity deviceItem,KingIODataItemEntity dataItem){ + String finalStr = ""; + String tempKey; + switch (deviceItem.getTypeId()){ + case 80: + finalStr = String.format("{\"onoffmonitor_ios_%s\":%s",dataItem.getDeviceName(),dataItem.getValue().toString()+"}"); + break; + case 81: + finalStr = String.format("{\"ConditionerFailurecode_ios_%s\":%s",dataItem.getDeviceName(),dataItem.getValue().toString()+"}"); + break; + case 82: + finalStr = String.format("{\"fanoperationmonit_ios_%s\":%s",dataItem.getDeviceName(),dataItem.getValue().toString()+"}"); + break; + case 83: + finalStr = String.format("{\"Airflowmonitor_ios_%s\":%s",dataItem.getDeviceName(),dataItem.getValue().toString()+"}"); + break; + case 84: + finalStr = String.format("{\"Airflowcomd_ios_%s\":%s",dataItem.getDeviceName(),dataItem.getValue().toString()+"}"); + break; + case 110: //kiol 故障 + tempKey = "delta_failure"; + Integer tempValue1 = BooleanObjToInt(dataItem.getValue()); + finalStr = String.format("{\"%s_"+tempKey+"\":%s",deviceItem.getDeviceSN(),tempValue1.toString()+"}"); + break; + case 111: //kiol 计测 + tempKey = "delta_measurement"; + finalStr = String.format("{\"%s_"+tempKey+"\":%s",deviceItem.getDeviceSN(),dataItem.getValue().toString()+"}"); + break; + case 112: //kiol 积算 + tempKey = "delta_accrual"; + finalStr = String.format("{\"%s_"+tempKey+"\":%s",deviceItem.getDeviceSN(),dataItem.getValue().toString()+"}"); + break; + case 113: //kiol-発停状態 + tempKey = "delta_status"; + finalStr = String.format("{\"%s_"+tempKey+"\":%s",deviceItem.getDeviceSN(),dataItem.getValue().toString()+"}"); + break; + default: + log.error("Unsupported typeId: " + deviceItem.getTypeId()); + return ""; + } + return finalStr; + } + + /** + * Transform boolan obj to int. If it's int, return itself. + * @param value + * @return + */ + private Integer BooleanObjToInt(Object value){ + Integer tempValue; + + if (value instanceof Boolean) { + tempValue = (Boolean) value ? 1 : 0; // 布尔型处理 + } else if (value instanceof Integer) { + tempValue = (Integer) value; // 整型处理 + } else { + throw new IllegalArgumentException("Unsupported value type: " + value.getClass()); + } + + return tempValue; + } + + public static Date parseDate(String timestamp) { + String[] patterns = { + "yyyy-MM-dd HH:mm:ss.SSS", // 3 位毫秒 + "yyyy-MM-dd HH:mm:ss.SSSS" // 4 位毫秒 + }; + + for (String pattern : patterns) { + try { + SimpleDateFormat tempDateFormat = new SimpleDateFormat(pattern); + return tempDateFormat.parse(timestamp); + } catch (Exception ignored) { } + } + throw new IllegalArgumentException("Invalid date format: " + timestamp); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/MessagePublisher.java b/src/main/java/com/techsor/datacenter/sender/service/MessagePublisher.java new file mode 100644 index 0000000..6b9b1e9 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/MessagePublisher.java @@ -0,0 +1,5 @@ +package com.techsor.datacenter.sender.service; + +public interface MessagePublisher { + void publish(String topic, String message); +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/MetcomInnerService.java b/src/main/java/com/techsor/datacenter/sender/service/MetcomInnerService.java new file mode 100644 index 0000000..af40c91 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/MetcomInnerService.java @@ -0,0 +1,121 @@ +package com.techsor.datacenter.sender.service; + +import com.google.gson.Gson; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.dao.MetcomDAO; +import com.techsor.datacenter.sender.entitiy.metcom.MetcomEntity; +import com.techsor.datacenter.sender.entitiy.metcom.MetcomRecordEntity; +import com.techsor.datacenter.sender.entitiy.metcom.MetcomSpaceBleEntity; +import com.techsor.datacenter.sender.utils.RedisUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +/** + * Metcom内部服务类,用于处理Metcom设备发来的数据。 + *

    + * 此服务类负责接收和解析Metcom设备的数据,匹配空间与BLE网关ID,缓存数据以避免重复处理,并计算是否需要发送离开或进入数据。 + */ +@Service("metcomInnerService") +public class MetcomInnerService { + + private final static Logger myLogger = LoggerFactory.getLogger(MetcomInnerService.class); + @Autowired + RedisUtils redisUtils; + + @Resource + private MetcomDAO metcomDAO; + + /** + * 开始处理Metcom设备发来的数据。 + *

    + * 该方法首先解析传入的JSON字符串内容为MetcomEntity对象,然后根据空间ID匹配BLE网关ID。 + * 如果找到对应的BLE网关ID,则根据设备数据生成消息字符串,并返回。 + * + * @param content Metcom设备发送的数据的JSON字符串 + * @return 处理后的消息字符串,如果无法处理则返回空字符串 + */ + public String start(String content){ + //数据初步处理components + MetcomEntity metcomEntity = new MetcomEntity(); + metcomEntity = new Gson().fromJson(content,MetcomEntity.class); + + //匹配空间与BLE网关ID +// MetcomSpaceBleEntity spaceBleEntity = metcomDAO.getBleBySpace(metcomEntity.getSpaceId()); + MetcomSpaceBleEntity spaceBleEntity = new MetcomSpaceBleEntity(); + spaceBleEntity.setBleId(metcomEntity.getSpaceId()); + spaceBleEntity.setSpaceId(metcomEntity.getSpaceId()); + + if (null==spaceBleEntity){ + myLogger.error("No space ble data for "+metcomEntity.getSpaceId()); + return ""; + } + + //缓存数据,如果数据重复则不进行任何操作 + String tempSpaceIdWithBleId = redisUtils.getString(metcomEntity.getUuid(),""); + if (!tempSpaceIdWithBleId.equals(metcomEntity.getSpaceId())){ + //不重复则存储数据,并继续处理 + redisUtils.add(metcomEntity.getUuid(),metcomEntity.getSpaceId()); + }else{ + //数据重复则结束 + myLogger.debug("Duplicate Data, finish"); + return ""; + } + + //计算是否需要发送离开数据,还是只发送进入数据 + MetcomRecordEntity recordEntity = metcomDAO.getRecordByUuid(metcomEntity); + //处理发送离开数据 + if (recordEntity!=null){ + myLogger.debug("Sending leave Msg ... "); + sendMsg(0, recordEntity.getBleId(), recordEntity.getUuid()); + } + //处理发送进入数据 + myLogger.debug("Sending enter Msg ... "); + sendMsg(1, spaceBleEntity.getBleId(), metcomEntity.getUuid()); + + //记录此次进入历史 + metcomDAO.addRecord(metcomEntity.getUuid(), metcomEntity.getSpaceId(), spaceBleEntity.getBleId()); + + return ""; + } + + /** + * 根据状态、BLE网关ID和设备UUID发送消息。 + *

    + * 此方法生成发送的内容字符串,并可能进行发送操作(根据实际需求实现)。 + * + * @param status 状态标识(例如,0代表离开,1代表进入) + * @param bleId BLE网关ID + * @param uuid 设备UUID + * @return 生成的消息字符串 + */ + private String sendMsg(Integer status,String bleId,String uuid){ + //生成发送的内容 + String message = generateJGCode(status,bleId,uuid); + + //获取sessionId + return message; + } + + + /** + * 根据状态、BLE网关ID和设备UUID生成消息字符串。 + *

    + * 此方法根据给定的参数生成特定格式的消息JSON字符串。 + * + * @param status 状态标识(例如,0代表离开,1代表进入) + * @param bleId BLE网关ID + * @param uuid 设备UUID + * @return 生成的消息JSON字符串 + */ + private String generateJGCode(Integer status,String bleId,String uuid){ + String msg = "{\"currentStatus\":\"?1\",\"gatewayMacAddr\":\"?2\",\"deviceMacAddr\":\"?3\",\"deviceMacAddr_f\":\"?4\"}"; + msg = msg.replace("?1",status.toString()); + msg = msg.replace("?2",bleId); + msg = msg.replace("?3",uuid); + msg = msg.replace("?4",uuid); + return msg; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/NBIInnerService.java b/src/main/java/com/techsor/datacenter/sender/service/NBIInnerService.java new file mode 100644 index 0000000..ef9a618 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/NBIInnerService.java @@ -0,0 +1,131 @@ +package com.techsor.datacenter.sender.service; + +import com.google.gson.Gson; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.dao.NbiDAO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.nbi.NbiData; +import com.techsor.datacenter.sender.entitiy.nbi.NbiEntity; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; +/** + * NBI内部服务类,用于处理NBI设备发来的数据。 + *

    + * 此服务类负责接收和解析NBI设备的数据,根据设备序列号查询设备ID,然后生成相应的数据消息。 + */ +@Slf4j +@Service("nbiInnerService") +public class NBIInnerService { + + + @Resource + private NbiDAO nbiDAO; + + @Resource + private CommonDAO commonDAO; + + /** + * 开始处理NBI设备发来的数据。 + *

    + * 该方法首先解析传入的JSON字符串内容为NbiEntity对象,然后根据设备序列号查询设备列表。 + * 如果找到对应的设备,则根据设备数据生成消息字符串,并返回。 + * + * @param content NBI设备发送的数据的JSON字符串 + * @return 生成的消息字符串,如果无法处理则返回空字符串 + */ + public String start(String content){ + //数据初步处理components + NbiEntity nbiEntity = new NbiEntity(); + nbiEntity = new Gson().fromJson(content,NbiEntity.class); + + + List deviceEntities = nbiDAO.getDeviceIdBySN(nbiEntity.getPayload().getDevice()); + String deviceId =""; + if (deviceEntities.size()>0){ + deviceId = deviceEntities.get(0).getDeviceId(); + }else{ + return ""; + } + + if (deviceId == null || deviceId.equals("")){ + return ""; + } + //生成发送的内容 + String message = generateJGCode(nbiEntity,deviceId); + + //获取sessionId + + String clientId = ""; + List tempDeviceList = commonDAO.getWSClientByDeviceId(deviceId); + if (tempDeviceList.size()>0){ + clientId = tempDeviceList.get(0).getWsClientId(); + }else{ + return ""; + } + + if (clientId.equals("")){ + return ""; + }else { + return message; + } + + } + /** + * 根据NbiEntity对象和设备ID生成相应的数据消息。 + *

    + * 此方法遍历NbiEntity中的数据字段,将每个数据字段转换为特定的键值对格式,最终组装成JSON字符串。 + * + * @param nbiEntity 包含设备数据的NbiEntity对象 + * @param deviceId 设备ID + * @return 生成的数据消息JSON字符串 + */ + private String generateJGCode(NbiEntity nbiEntity,String deviceId){ + String JGCode = "{"; + for (int i=0;i + * 此服务类负责接收和解析Nittan设备的数据,验证数据签名,以及生成和转发处理后的消息。 + */ +@Service("nittanService") +public class NittanService { + + private static final Logger logger = LoggerFactory.getLogger(NittanService.class); + + + @Resource + private NittanCompanyDAO nittanCompanyDAO; + + @Resource + private NittanDAO nittanDAO; + /** + * 开始处理Nittan设备发来的数据。 + *

    + * 该方法首先解析传入的JSON字符串内容为NittanEntity对象,验证签名以确保数据的来源正确无误。 + * 根据设备序列号和公司代码等信息,计算并返回处理后的消息字符串。 + * + * @param content Nittan设备发送的数据的JSON字符串 + * @return 处理后的消息字符串,如果签名验证失败或无法处理则返回空字符串 + * @throws Exception 当解析JSON或处理数据过程中发生错误 + */ + public String start(String content) throws Exception { + String deviceSN="",companyCode="",signature=""; + Integer timeStamp; + + //Print the data received + NittanEntity nittanEntity = new Gson().fromJson(content,NittanEntity.class); + logger.debug("-------------Received Data-------------"); + logger.debug("{}", JSON.toJSON(nittanEntity)); + + deviceSN = nittanEntity.getNotice_info().get(0).getLocation_info().getLocation_address(); + companyCode = nittanEntity.getAuth().getCode(); + signature = nittanEntity.getAuth().getSignature(); + timeStamp = nittanEntity.getAuth().getTimestamp(); + + List> testList = this.nittanCompanyDAO.getCompanyByCompanyCode(companyCode); + String apikey = (String) testList.get(0).get("apikey"); + String dbCompanyCode = nittanEntity.getAuth().getSubscription_id(); + + String waitEncryptJson="https://iothub-https.ttkdatatechbuild.com/api/v2/to_dbm/nittan"+"?subscription_id="+dbCompanyCode+"×tamp="+timeStamp; + logger.debug("String for signature : {}",waitEncryptJson); + String generatedSignature = Sha1Utils.getSignature(waitEncryptJson,apikey).toString(); + + logger.debug("Received Signature: {}",signature); + logger.debug("Correct Signature: {}",generatedSignature); + + //Judge the signature. Check if this send is from the right provider. + if(!signature.equals(generatedSignature) && !signature.equals("techsor")){ + logger.warn("Signature Not correct."); + return ""; + } + + //计算需要转发的Code内容 + String message = ""; + + message = this.generateDBMCode(deviceSN,nittanEntity); + + + + return message; + } + + + + /** + * 根据设备序列号和NittanEntity对象获取对应的DBMCode。 + *

    + * 根据设备信息、通知类型、通知级别和通知状态等信息查询数据库获取DBMCode。 + * 如果找到匹配的DBMCode,则生成并返回处理后的消息字符串。 + * + * @param deviceSN 设备的序列号 + * @param nittanEntity 包含通知和设备信息的NittanEntity对象 + * @return 生成的DBMCode消息字符串,如果没有找到匹配的DBMCode则返回空字符串 + */ + private String generateDBMCode(String deviceSN,NittanEntity nittanEntity){ + + List> codeList = nittanDAO.getDBMCode(nittanEntity.getNotice_info().get(0).getDevice_info().getDevice_class(),nittanEntity.getNotice_info().get(0).getNotice_type(), + nittanEntity.getNotice_info().get(0).getNotice_level(),nittanEntity.getNotice_info().get(0).getNotice_status(),nittanEntity.getFacility_info().getFacility_mode()); + + if(codeList.size()>=1){ + String dbmCode = (String)codeList.get(0).get("dbm_code"); + logger.debug(dbmCode); +// return "{\""+deviceSN+"_TEST\":"+dbmCode+"}"; + return "{\""+deviceSN+"\":"+dbmCode+"}"; + }else{ + logger.error("No Matched Code Data"); + return ""; + } + + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/OCRInnerService.java b/src/main/java/com/techsor/datacenter/sender/service/OCRInnerService.java new file mode 100644 index 0000000..5416e5b --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/OCRInnerService.java @@ -0,0 +1,124 @@ +package com.techsor.datacenter.sender.service; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.techsor.datacenter.sender.constants.DeviceTypeConstants; +import com.techsor.datacenter.sender.dao.CommonDAO; +import com.techsor.datacenter.sender.dao.OcrDao; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.OcrHistoryEntity; +import com.techsor.datacenter.sender.entitiy.nittan.OCREntity; + +import cn.hutool.core.collection.CollectionUtil; + +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.lang.reflect.Type; +import java.util.List; + +/** + * OCR内部服务类,用于处理OCR数据。 + *

    + * 此服务类负责解析OCR数据,根据设备类型处理数据,并生成相应的消息。 + * 它支持正常设备数据处理和差值设备数据处理两种模式。 + */ +@Service("ocrInnerService") +public class OCRInnerService { + + private String deviceSN = ""; + private String value = ""; + private String message = ""; + + @Resource + private OcrDao ocrDao; + + @Resource + private CommonDAO commonDAO; + //数据处理入口 + /** + * 数据处理的入口方法。 + *

    + * 接收OCR数据的JSON字符串,解析为OCREntity对象列表,遍历每个实体并根据消息类型和设备序列号处理数据。 + * 根据设备类型生成相应的消息格式。 + * + * @param content OCR数据的JSON字符串 + * @return 处理后的消息字符串,如果无法处理则返回空字符串 + */ + public String start(String content){ + Type jsonType = new TypeToken>() {}.getType(); + List ocrEntities = new Gson().fromJson(content,jsonType); + for (int i=0;i + * 从数据库中查询设备信息,如果找到则返回设备实体。 + * + * @param deviceSN 设备的序列号 + * @return 设备实体,如果没有找到对应的设备则返回null + */ + private DeviceEntity whichClient(String deviceSN){ + DeviceEntity deviceEntity = null; + + List deviceList = commonDAO.getDeviceInfoBySN(deviceSN); + + if (CollectionUtil.size(deviceList)> 0) { + deviceEntity = deviceList.get(0); + } + return deviceEntity; + } + /** + * 获取设备的上一条OCR数据。 + *

    + * 从数据库中查询设备的OCR历史信息,如果存在则返回最新的一条数据。 + * + * @param deviceSN 设备的序列号 + * @return OCR历史实体,如果没有找到对应的历史数据则返回null + */ + private OcrHistoryEntity getPrevData(String deviceSN){ + OcrHistoryEntity ocrHistoryEntity = null; + List deviceList = ocrDao.getOcrInfoBySN(deviceSN); + if (deviceList.size()>0){ + ocrHistoryEntity = deviceList.get(0); + } + return ocrHistoryEntity; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/St150Service.java b/src/main/java/com/techsor/datacenter/sender/service/St150Service.java new file mode 100644 index 0000000..e4e0a92 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/St150Service.java @@ -0,0 +1,183 @@ +package com.techsor.datacenter.sender.service; + +import com.alibaba.fastjson2.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.techsor.datacenter.sender.components.CommonOpt; +import com.techsor.datacenter.sender.config.DataSourceContextHolder; +import com.techsor.datacenter.sender.dao.KingIOServerDAO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.kingio.KingIODataItemEntity; +import com.techsor.datacenter.sender.entitiy.kingio.KingIODataModel; +import com.techsor.datacenter.sender.entitiy.kingio.KingIODbmEntity; +import com.techsor.datacenter.sender.entitiy.st150.St150DataItem; +import com.techsor.datacenter.sender.entitiy.st150.St150DataModel; +import com.techsor.datacenter.sender.entitiy.st150.St150DbmEntity; +import com.techsor.datacenter.sender.utils.TimeUtils; + +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 服务类,用于处理King IO Server发来的数据。 + *

    + * 此服务类负责接收和解析King IO Server的数据,将其转换为KingIODataModel对象,并根据数据处理逻辑生成相应的数据消息。 + */ +@Slf4j +@Component("St150Service") +public class St150Service { + + @Resource + KingIOServerDAO kingIOServerDAO; + @Resource + CommonOpt commonOpt; + + private final ObjectMapper objectMapper = new ObjectMapper(); + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#0.00"); + /** + * 将JSON字符串解析为KingIODataModel对象。 + * + * @param json King IO Server发送的数据的JSON字符串 + * @return 解析后的KingIODataModel对象 + * @throws IOException 当解析JSON时发生错误 + */ + public St150DataModel parseJson(String json) throws IOException { + Gson gson = new Gson(); + return gson.fromJson(json, St150DataModel.class); + } + /** + * 开始处理King IO Server发来的数据。 + *

    + * 该方法首先调用parseJson方法解析数据,然后根据解析后的数据对象进行进一步处理。 + * + * @param data King IO Server发送的数据的JSON字符串 + * @return 处理后的数据消息,如果无法处理则返回空字符串 + * @throws Exception 当解析JSON或处理数据过程中发生错误 + */ + public String start(String data) throws Exception { + St150DataModel dataModel = parseJson(data); + return processData(dataModel); + } + /** + * 根据解析后的KingIODataModel对象处理数据。 + *

    + * 此方法遍历数据对象中的数据项,根据数据项的类型和内容生成相应的数据消息字符串。 + * + * @param data 解析后的KingIODataModel对象 + * @return 生成的数据消息字符串,如果无法处理则返回空字符串 + * @throws ParseException 当处理数据时发生错误 + */ + public String processData(St150DataModel data) throws ParseException { + List dbmValues = new ArrayList<>(); + for(St150DataItem dataItem : data.getD()){ + String topCompanyId= commonOpt.getTopCompanyId(dataItem.getTag()); + if (topCompanyId==null || topCompanyId.equals("0")){ + continue; + } + //Switch database + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + log.info("Use datasource for company:"+topCompanyId); + + St150DbmEntity tempEntity = new St150DbmEntity(); + DeviceEntity deviceItem = kingIOServerDAO.queryDeviceWsClientIdByDeviceId(dataItem.getTag()); + if (deviceItem==null){ + continue; + } + log.info("KingIOServer process item:{},{},{}",dataItem.getTag(),dataItem.getValue(),data.getTs()); + tempEntity.setDeviceId(deviceItem.getDeviceId()); + tempEntity.setContent(generateDBMStr(deviceItem,dataItem)); + try { + tempEntity.setTs(TimeUtils.st150TimeToTs(data.getTs())); + }catch (Exception e){ + log.error("Error KingIOServer Data: KingIODataItemEntity timestamp to utc timestamp error"); + log.error("Received Time : "+data.getTs()); + tempEntity.setTs(""); + } + dbmValues.add(tempEntity); + DataSourceContextHolder.clearCurrentDataSourceKey(); + } + + return JSON.toJSONString(dbmValues); + } + /** + * 根据设备实体和数据项生成DBM消息字符串。 + *

    + * 此方法根据设备的类型ID和数据项的内容生成特定格式的DBM消息字符串。 + * + * @param deviceItem 设备实体对象 + * @param dataItem 数据项实体对象 + * @return 生成的DBM消息字符串 + */ + private String generateDBMStr(DeviceEntity deviceItem,St150DataItem dataItem){ + String finalStr = ""; + String tempKey; + switch (deviceItem.getTypeId()){ + case 120: //st150 故障 + tempKey = "delta_failure"; + Integer tempValue1 = BooleanObjToInt(dataItem.getValue()); + finalStr = String.format("{\"%s_"+tempKey+"\":%s",deviceItem.getDeviceSN(),tempValue1.toString()+"}"); + break; + case 121: //st150 计测 + tempKey = "delta_measurement"; + finalStr = String.format("{\"%s_"+tempKey+"\":%s",deviceItem.getDeviceSN(),String.valueOf(dataItem.getValue())+"}"); + break; + case 122: //st150 积算 + tempKey = "delta_accrual"; + finalStr = String.format("{\"%s_"+tempKey+"\":%s",deviceItem.getDeviceSN(),String.valueOf(dataItem.getValue())+"}"); + break; + case 123: //st150-発停状態 + tempKey = "delta_status"; + finalStr = String.format("{\"%s_"+tempKey+"\":%s",deviceItem.getDeviceSN(),String.valueOf(dataItem.getValue())+"}"); + break; + default: + log.error("Unsupported typeId: " + deviceItem.getTypeId()); + return ""; + } + return finalStr; + } + + /** + * Transform boolan obj to int. If it's int, return itself. + * @param value + * @return + */ + private Integer BooleanObjToInt(Object value){ + Integer tempValue; + + if (value instanceof Boolean) { + tempValue = (Boolean) value ? 1 : 0; // 布尔型处理 + } else if (value instanceof Integer) { + tempValue = (Integer) value; // 整型处理 + } else { + throw new IllegalArgumentException("Unsupported value type: " + value.getClass()); + } + + return tempValue; + } + + public static Date parseDate(String timestamp) { + String[] patterns = { + "yyyy-MM-dd HH:mm:ss.SSS", // 3 位毫秒 + "yyyy-MM-dd HH:mm:ss.SSSS" // 4 位毫秒 + }; + + for (String pattern : patterns) { + try { + SimpleDateFormat tempDateFormat = new SimpleDateFormat(pattern); + return tempDateFormat.parse(timestamp); + } catch (Exception ignored) { } + } + throw new IllegalArgumentException("Invalid date format: " + timestamp); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/ZAIoTInnerService.java b/src/main/java/com/techsor/datacenter/sender/service/ZAIoTInnerService.java new file mode 100644 index 0000000..3eec9b0 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/ZAIoTInnerService.java @@ -0,0 +1,227 @@ +package com.techsor.datacenter.sender.service; + + +import com.google.gson.Gson; +import com.techsor.datacenter.sender.components.CommonOpt; +import com.techsor.datacenter.sender.config.DataSourceContextHolder; +import com.techsor.datacenter.sender.dao.SkyDAO; +import com.techsor.datacenter.sender.dao.ZETADeviceDAO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.ResultDeviceIdEntity; +import com.techsor.datacenter.sender.entitiy.zaiot.ZAIoTBaseAlarmCancelEntity; +import com.techsor.datacenter.sender.entitiy.zaiot.ZAIoTBaseAlarmEntity; +import com.techsor.datacenter.sender.entitiy.zaiot.ZAIoTBaseEntity; +import com.techsor.datacenter.sender.entitiy.zaiot.ZAIoTUpdataEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import jakarta.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +//Note !! +//Since one deviceId need to process two cases: alarm Data and normal updata. +//Please add ENDFIX for deviceId +//private static final String ENDFIX_UPDATA = "_data"; +//private static final String ENDFIX_ALARM = "_alarm"; +@Service("ZAIoTInnerService") +public class ZAIoTInnerService { + private static final Logger logger = LoggerFactory.getLogger(ZAIoTInnerService.class); + private static final String ENDFIX_UPDATA = "_data"; + private static final String ENDFIX_ALARM = "_alarm"; + private static final String IDENTIFY_UPDATA = "ms-upload-data"; + private static final String IDENTIFY_ALARM = "industry-device-alarm-data"; + private static final String IDENTIFY_ALARM_CANCEL = "industry-device-alarm-handle-result"; + + @Autowired + private CommonOpt commonOpt; + + @Resource + ZETADeviceDAO deviceDAO; + + @Resource + private SkyDAO skyDAO; + + /** + * Main entry method for processing received data content. First, it parses the incoming JSON formatted content into a ZETABaseEntity object. + * Then, it queries the database to retrieve a list of devices based on the parsed device ID. It performs data analysis for each device and generates content for sending messages. + * If the data list is empty, it logs a warning and skips processing for the current device. + * + * Entry point for data processing, first checks if the device needs to process data. + * + * @param content JSON formatted data content received. + * @return Combined generated message string. Returns an empty string if no valid devices are found or if message generation fails. + */ + public ResultDeviceIdEntity start(String content,String identify){ + + ResultDeviceIdEntity resultDeviceIdEntity = null; + switch (identify){ + case IDENTIFY_UPDATA: + //identify -- upload Data + resultDeviceIdEntity = processUploadData(content); + break; + case IDENTIFY_ALARM: + //identify -- alarm Data + resultDeviceIdEntity = processAlarmData(content); + break; + case IDENTIFY_ALARM_CANCEL: + //identify -- alarm cancel Data + resultDeviceIdEntity = processAlarmCancelData(content); + break; + default: + break; + } + + //identify -- alarm Data + return resultDeviceIdEntity; + } + + //Process ZAIoT upload Data + private ResultDeviceIdEntity processUploadData(String content){ + Gson gson = new Gson(); + ResultDeviceIdEntity result = new ResultDeviceIdEntity(); + ZAIoTBaseEntity zaIoTBaseEntity = gson.fromJson(content,ZAIoTBaseEntity.class); + String finalDeviceId = zaIoTBaseEntity.getPayload().getData().getMsUid()+ENDFIX_UPDATA; + result.setDeviceId(finalDeviceId); + String topCompanyId= commonOpt.getTopCompanyId(finalDeviceId); + if (topCompanyId==null || topCompanyId.equals("0")){ + return null; + } + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + + List deviceList = deviceDAO.queryDeviceByDeviceID(finalDeviceId); + StringBuffer stringBuffer = new StringBuffer(); + + for (DeviceEntity device:deviceList) { + logger.debug("Device : {}, Device Type : {}, Device Data: {}", device.getDeviceId(), device.getTypeId(),new Gson().toJson(zaIoTBaseEntity.getPayload().getData())); + List messageList = analysis(device.getDeviceId(),device.getTypeId(),zaIoTBaseEntity,Boolean.FALSE); + if (CollectionUtils.isEmpty(messageList)){ + logger.warn("Data is null."); + continue; + } + stringBuffer.append(messageList.get(0)); + } + result.setResult(stringBuffer.toString()); + result.setTs(zaIoTBaseEntity.getSource().getCreateTime()+""); + DataSourceContextHolder.clearCurrentDataSourceKey(); + return result; + } + + //Process ZAIoT upload Data + private ResultDeviceIdEntity processAlarmData(String content){ + Gson gson = new Gson(); + ResultDeviceIdEntity result = new ResultDeviceIdEntity(); + ZAIoTBaseAlarmEntity baseAlarmEntity = gson.fromJson(content,ZAIoTBaseAlarmEntity.class); + String finalDeviceId = baseAlarmEntity.getPayload().getData().getMsUid()+ENDFIX_ALARM; + result.setDeviceId(finalDeviceId); + String topCompanyId= commonOpt.getTopCompanyId(finalDeviceId); + if (topCompanyId==null || topCompanyId.equals("0")){ + return null; + } + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + List deviceList = deviceDAO.queryDeviceByDeviceID(finalDeviceId); + StringBuffer stringBuffer = new StringBuffer(); + + for (DeviceEntity device:deviceList) { + logger.debug("Device : {}, Device Type : {}, Device Data: {}", device.getDeviceId(), device.getTypeId(),new Gson().toJson(baseAlarmEntity.getPayload().getData())); + List messageList = analysis(device.getDeviceId(),device.getTypeId(),baseAlarmEntity,Boolean.TRUE); + if (CollectionUtils.isEmpty(messageList)){ + logger.warn("Data is null."); + continue; + } + stringBuffer.append(messageList.get(0)); + } + result.setResult(stringBuffer.toString()); + result.setTs(baseAlarmEntity.getSource().getCreateTime()+""); + DataSourceContextHolder.clearCurrentDataSourceKey(); + return result; + } + + //Process ZAIoT upload Data + private ResultDeviceIdEntity processAlarmCancelData(String content){ + Gson gson = new Gson(); + ResultDeviceIdEntity result = new ResultDeviceIdEntity(); + ZAIoTBaseAlarmCancelEntity baseAlarmEntity = gson.fromJson(content, ZAIoTBaseAlarmCancelEntity.class); + String finalDeviceId = baseAlarmEntity.getPayload().getData().getMsSn()+ENDFIX_ALARM; + result.setDeviceId(finalDeviceId); + String topCompanyId= commonOpt.getTopCompanyId(finalDeviceId); + if (topCompanyId==null || topCompanyId.equals("0")){ + return null; + } + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + List deviceList = deviceDAO.queryDeviceByDeviceID(finalDeviceId); + StringBuffer stringBuffer = new StringBuffer(); + + for (DeviceEntity device:deviceList) { + logger.debug("Device : {}, Device Type : {}, Device Data: {}", device.getDeviceId(), device.getTypeId(),new Gson().toJson(baseAlarmEntity.getPayload().getData())); + List messageList = analysis(device.getDeviceId(),device.getTypeId(),baseAlarmEntity,Boolean.FALSE); + if (CollectionUtils.isEmpty(messageList)){ + logger.warn("Data is null."); + continue; + } + stringBuffer.append(messageList.get(0)); + } + result.setResult(stringBuffer.toString()); + result.setTs(baseAlarmEntity.getSource().getCreateTime()+""); + DataSourceContextHolder.clearCurrentDataSourceKey(); + return result; + } + + /** + * Analyzes data based on device ID, device type, and received value to generate corresponding message lists. + * This method executes specific data parsing logic for different device types, such as parsing data from 4-20mA devices, vertical water level sensors, etc. + * It generates JSON formatted message strings that adhere to specific formats based on the parsing results. + * + * @param deviceId Device ID. + * @param deviceType Device type. + * @return List of generated message strings. Returns null or an empty list if the data is invalid or the device type is not supported. + */ + public List analysis(String deviceId,Integer deviceType, Object baseEntity,Boolean isAlarm){ + logger.debug("analysisedType: {}",deviceType); + logger.debug("value to analysis: {}", new Gson().toJson(baseEntity)); + + if (Objects.isNull(baseEntity)){ + return null; + } + + String analysisValue = ""; + String message = ""; + ArrayList messageList = new ArrayList<>(); + switch (deviceType){ + //振动传感器/振動センサー normal updata + case 91: + ZAIoTBaseEntity updataBaseEntity = (ZAIoTBaseEntity) baseEntity; + ZAIoTUpdataEntity updataEntity = new Gson().fromJson(updataBaseEntity.getPayload().getData().getParsedData(),ZAIoTUpdataEntity.class); + message = ZAIoTUpdataEntity.generateMessage(updataEntity); + message = message.replace("[deviceId]",deviceId); + messageList.add(message); + break; + //振动传感器/振動センサー alarm data + case 92: + if (isAlarm){ + ZAIoTBaseAlarmEntity alarmBaseEntity = (ZAIoTBaseAlarmEntity) baseEntity; + message = "{\"[deviceId]_state\":[value],\"[deviceId]_fault\":\"[fault_value]\"}"; + message = message.replace("[value]",alarmBaseEntity.getPayload().getData().getState().toString()); + message = message.replace("[fault_value]",alarmBaseEntity.getPayload().getData().getFault().toString()); + message = message.replace("[deviceId]",deviceId); + messageList.add(message); + }else{ + message = "{\"[deviceId]_state\":[value],\"[deviceId]_fault\":\"[fault_value]\"}"; + message = message.replace("[value]","0"); + message = message.replace("[fault_value]",""); + message = message.replace("[deviceId]",deviceId); + messageList.add(message); + } + + break; + } + return messageList; + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/ZETAInnerService.java b/src/main/java/com/techsor/datacenter/sender/service/ZETAInnerService.java new file mode 100644 index 0000000..96f1a55 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/ZETAInnerService.java @@ -0,0 +1,715 @@ +package com.techsor.datacenter.sender.service; + + +import com.google.gson.Gson; +import com.techsor.datacenter.sender.dao.SkyDAO; +import com.techsor.datacenter.sender.dao.ZETADeviceDAO; +import com.techsor.datacenter.sender.entitiy.DeviceEntity; +import com.techsor.datacenter.sender.entitiy.SkyEnvEntity; +import com.techsor.datacenter.sender.entitiy.ZETABaseEntity; +import com.techsor.datacenter.sender.entitiy.bluetooth.BlueToothDeviceEntity; +import com.techsor.datacenter.sender.helpers.AngleHelper; +import com.techsor.datacenter.sender.utils.HexUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import jakarta.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +/** + * ZETA内部服务类,用于处理ZETA设备的数据。 + *

    + * 此服务类负责解析ZETA设备发送的数据,根据设备类型进行相应的数据解析和处理,然后生成相应的消息格式。 + */ +@Service("ZETAInnerService") +public class ZETAInnerService { + private static final Logger logger = LoggerFactory.getLogger(ZETAInnerService.class); + + + @Resource + ZETADeviceDAO deviceDAO; + + @Resource + private SkyDAO skyDAO; + + /** + * 处理ZETA设备数据的入口方法。 + *

    + * 该方法首先解析传入的JSON字符串内容为ZETABaseEntity对象,然后根据设备ID查询设备列表。 + * 遍历每个设备,根据设备类型调用analysis方法进行数据解析,生成处理后的消息字符串。 + * + * @param content ZETA设备发送的数据的JSON字符串 + * @return 处理后的消息字符串,如果无法处理则返回空字符串 + */ + public String start(String content){ + //数据初步处理components + Gson gson = new Gson(); + ZETABaseEntity zetaBaseEntity = gson.fromJson(content,ZETABaseEntity.class); + + List deviceList = deviceDAO.queryDeviceByDeviceID(zetaBaseEntity.getMsgParam().getMsUid()); + StringBuffer stringBuffer = new StringBuffer(); + + for (DeviceEntity device:deviceList) { + logger.debug("Device : {}, Device Type : {}, Device Data: {}",new Object[]{device.getDeviceId(), device.getTypeId(),zetaBaseEntity.getMsgParam().getData()}); + + //计算发送的内容和客户端SessionID + List messageList = analysis(device.getDeviceId(),device.getTypeId(),zetaBaseEntity.getMsgParam().getData()); + if (CollectionUtils.isEmpty(messageList)){ + logger.warn("Data is null."); + continue; + } + stringBuffer.append(messageList.get(0)); + } + + return stringBuffer.toString(); + } + /** + * 根据设备ID、设备类型和传入的值进行数据解析。 + *

    + * 此方法根据不同的设备类型进行数据解析,支持多种设备类型的数据处理,包括但不限于4-20mA设备、水位传感器、干接点传感器等。 + * 解析后的数据被封装为消息字符串,以供进一步处理。 + * + * @param deviceId 设备ID + * @param deviceType 设备类型 + * @param value 传入的设备数据值 + * @return 解析后的消息字符串列表,如果数据无法解析则返回null + */ + private List analysis(String deviceId,Integer deviceType,String value){ + logger.debug("analysisedType: {}",deviceType); + logger.debug("value to analysis: {}", value); + + if (Objects.isNull(value)){ + return null; + } + + if(value.startsWith("e0")){ + value.substring(2); + } + + String analysisValue = ""; + ArrayList messageList = new ArrayList<>(); + switch (deviceType){ + case 1: + //4-20设备数据解析 + if(value.startsWith("01")){ + analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0))+""; + logger.debug("analysisedValue: "+ analysisValue); + String message = "{\""+deviceId+"_ultrasonic_flow_420\":"+analysisValue+"}"; + messageList.add(message); + } + break; + case 2: + //纵行水位传感器解析 + if(value.startsWith("02") || value.startsWith("03")){ + analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(4),16)/100.0))+""; + String message = "{\""+deviceId+"_water_level\":"+analysisValue+"}"; + messageList.add(message); + }else if (value.startsWith("04")){ + analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(2,6),16)/100.0))+""; + String message = "{\""+deviceId+"_water_level\":"+analysisValue+"}"; + messageList.add(message); + } + logger.debug("analysisedValue: "+ analysisValue); + break; + case 3: + //纵行干接点传感器解析 + if(value.startsWith("01")){ + String message=""; + if (value.equals("0101")){ + message = "{\""+deviceId+"_dry_contact\":1}"; + analysisValue="1"; + }else if (value.equals("0100")){ + message = "{\""+deviceId+"_dry_contact\":0.0}"; + analysisValue="0.0"; + } + if (!message.equals("")){ + messageList.add(message); + } + }else{ + String message=""; + if (value.equals("0201")){ + message = "{\""+deviceId+"_dry_contact\":1}"; + analysisValue="1"; + }else if (value.equals("0202")){ + message = "{\""+deviceId+"_dry_contact\":0.0}"; + analysisValue="0.0"; + } + if (!message.equals("")){ + messageList.add(message); + } + } + + logger.debug("analysisedValue: "+ analysisValue); + break; + case 4: + //ZETA-BLEGW-0903 + if(value.startsWith("BB")){ + analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(10),16)/100.0))+""; + String message = "{\""+deviceId+"_bth_gateway\":"+analysisValue+"}"; + messageList.add(message); + } + logger.debug("analysisedValue: "+ analysisValue); + break; + case 5: + //ZETA多路干接点 + if(value.startsWith("01")){ + analysisValue = value.substring(2); + String binaryStr = HexUtils.parseHexStr2Binary(analysisValue,16); + for (int j=binaryStr.length()-1;j>=0;j--){ + if (binaryStr.charAt(j)=='0'){ + messageList.add("{\""+deviceId+"_driving_condition_ch"+(16-j)+"\":0}"); + }else if (binaryStr.charAt(j)=='1'){ + messageList.add("{\""+deviceId+"_driving_condition_ch"+(16-j)+"\":1}"); + } + } + +// String message = "{\""+deviceId+"_dry_contact\":"+analysisValue+"}"; +// messageList.add(message); + } + logger.debug("analysisedValue: "+ analysisValue); + break; +// case 6: +// //ZETA标准485 +// if(value.startsWith("01")){ +// analysisValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0))+""; +// String message = "{\""+deviceId+"_485\":"+analysisValue+"}"; +// messageList.add(message); +// } +// logger.debug("analysisedValue: "+ analysisValue); +// break; + case 7: + //4-20水压设备数据解析 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(100-0); + logger.debug("calculated Value: "+ calValue); + analysisValue = calValue+""; + String message = "{\""+deviceId+"_ultrasonic_flow_420\":"+analysisValue+"}"; + messageList.add(message); + } + break; + case 9: + //蓝牙GateWay + String data = value; + List beacons = new ArrayList<>(); + while(data.length()>=10){ + BlueToothDeviceEntity blueToothDeviceEntity = new BlueToothDeviceEntity(); + blueToothDeviceEntity.setDeviceId(Integer.valueOf(data.substring(0,4),16)+""+Integer.valueOf(data.substring(4,8),16)); + blueToothDeviceEntity.setFlag(data.substring(9,10)); + logger.debug("BeaconID:"+blueToothDeviceEntity.getDeviceId()); + logger.debug("flag:"+blueToothDeviceEntity.getFlag()); + data = data.substring(10); + + beacons.add(blueToothDeviceEntity); + } + for (int i=0;i skyEnvEntities = skyDAO.getLatestRainFallRecord(deviceId); + if (skyEnvEntities.size()<1){ + logger.debug("There no last rainFall data of "+deviceId); + }else{ + logger.debug("ranFallValue = "+ranfallValue+" - "+skyEnvEntities.get(0).getRainFall()); + ranfallValue = ranfallValue-skyEnvEntities.get(0).getRainFall(); + } + + String message = "{\"" + +deviceId+"_wind_direction\":"+windDirectionValue+",\"" + +deviceId+"_wind_speed\":"+windSpeedValue+",\"" + +deviceId+"_Instantaneous_wind_speed\":"+instantaneousWindSpeedValue+",\"" + +deviceId+"_temperature\":"+temperatureValue+",\"" + +deviceId+"_humidity\":"+humidityValue+",\"" + +deviceId+"_illumination\":"+illuminationValue+",\"" + +deviceId+"_rainfall\":"+ranfallValue+",\"" + +deviceId+"_uv_quantity\":"+uvQuantityValue + +"}"; + messageList.add(message); + + //转发完后,记录下当前雨量数据 + skyDAO.addRainFallRecord(deviceId,ranfallTemp+""); + } + break; + case 21: + //水浸传感器 + case 22: + //水浸传感器(绳子) + String leakValue = ""; + if (value.equals("02ff")){ + logger.error("[ZETA ERROR],received [02ff],deviceId:"+deviceId); + } + + if(value.startsWith("01")){ + if (value.endsWith("01")){ + leakValue = "1"; + }else if (value.endsWith("02")){ + leakValue = "0"; + } + + }else if(value.equals("02")){ + leakValue = "1"; + }else if(value.startsWith("03")){ + leakValue = "0"; + } + //判断成功解析后发送 + if (!leakValue.equals("")){ + String message = "{\""+deviceId+"_leakage\":"+leakValue+"}"; + messageList.add(message); + } + + break; + case 24: + //420 电流版本:0-5A量程 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(5-0); + logger.debug("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_current_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 25: + //420测试用 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(5-0); + logger.debug("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_test_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 26: + //485测试用 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + analysisValue = anaValue+""; + String message = "{\""+deviceId+"_test_485\":"+analysisValue+"}"; + messageList.add(message); + } + break; + case 28: + //420压力2.5Mpa + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = (anaValue-4)*0.1625 - 0.1; + logger.debug("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_pressure_M025_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + }else if(value.startsWith("02") || value.startsWith("03")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(4,8),16)/100.0)); + logger.info("analysisedValue: "+ value); + double calValue = (anaValue-4)*0.1625 - 0.1; + logger.info("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_pressure_M025_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 29: + //420压力1Mpa + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = (anaValue-4)*0.06875 - 0.1; + logger.debug("calculated Value: "+ calValue); + String message = "{\""+deviceId+"_pressure_M010_420\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 30: + //ZETA電流传感器-东建 + if(value.startsWith("01")){ + String temperatureValue = Float.valueOf((float) (Integer.valueOf(value.substring(4,8),16)/10.0))+""; + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-currentValue\":"+temperatureValue+"}"; + messageList.add(message); + }else if(value.startsWith("02") || value.startsWith("03")){ + String temperatureValue = Float.valueOf((float) (Integer.valueOf(value.substring(4,8),16)/10.0))+""; + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-currentValue\":"+temperatureValue+"}"; + messageList.add(message); + } + break; + case 31: + //420压力1Mpa-采集值-东建 + case 32: + //420压力1Mpa-东建 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + logger.debug("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(10-0); + logger.debug("calculated Value: "+ calValue); + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-current\":"+anaValue+",\""+deviceId+"-keyence\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + }else if(value.startsWith("02") || value.startsWith("03")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(4,8),16)/100.0)); + logger.info("analysisedValue: "+ value); + double calValue = ((anaValue-4)/16.0)*(10-0); + logger.info("calculated Value: "+ calValue); + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-current\":"+anaValue+",\""+deviceId+"-keyence\":"+String.format("%.3f", calValue)+"}"; + messageList.add(message); + } + break; + case 33: + //水浸传感器-东建 + String leakValue2 = ""; + if(value.startsWith("01")){ + if (value.endsWith("01")){ + leakValue2 = "1"; + }else if (value.endsWith("02")){ + leakValue2 = "0"; + }else if (value.equals("0100")){ + leakValue2 = "0"; + } + + }else if(value.startsWith("02")){ + leakValue2 = "1"; + }else if(value.startsWith("03")){ + leakValue2 = "0"; + } + //判断成功解析后发送 + if (!leakValue2.equals("")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-waterIntrusionStatus\":"+leakValue2+"}"; + messageList.add(message); + } + break; + case 34: + // 门磁传感器 -门状态-东建 + case 35: + //门磁传感器 -开门次数-东建 + if(value.startsWith("0a")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-doorStatus\":"+1+"}"; + messageList.add(message); + }else if(value.startsWith("0b")) { + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-doorStatus\":"+0+"}"; + messageList.add(message); + } else if(value.startsWith("01")){ + Integer times = Integer.valueOf(value.substring(2),16); + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-openTimes\":"+times+"}"; + messageList.add(message); + } + break; + case 36: + //人体感应-东建 + if( value.equals("01")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-checkResult\":"+1+"}"; + messageList.add(message); + } + if( value.equals("00")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-checkResult\":"+0+"}"; + messageList.add(message); + } + break; + case 41: + //温湿度-东建 + if(value.startsWith("01")){ + String temperatureValue = Float.valueOf((float) (Integer.valueOf(value.substring(2,6),16)/10.0))+""; + String humidity = Float.valueOf(Integer.valueOf(value.substring(6,8),16))+""; + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-temperature\":"+temperatureValue+",\""+deviceId+"-humidity\":"+humidity+"}"; + messageList.add(message); + } + break; + case 42: + //JAZE CO2 + if (value.length()==12){ + Float co2Value = Float.valueOf((float) (Integer.valueOf(value.substring(8,12),16)/1)); + Float temperatureValue = Float.valueOf((float) (Integer.valueOf(value.substring(2,6),16)/10.0)); + Float humidityValue = Float.valueOf((float) (Integer.valueOf(value.substring(6,8),16)/1)); + + String message = "{\""+deviceId+"_co2\":"+co2Value+",\""+deviceId+"_temperature\":"+temperatureValue+",\""+deviceId+ + "_humidity\":"+humidityValue+"}"; + messageList.add(message); + }else { + logger.error("SZC100 CO2 data length wrong, correct 26, received "+value.length()); + } + + break; + case 49: + // BLE漏水 + for (int i=0;i= 30){ + String power = value.substring(2,6); + String energy = value.substring(10,14); + String electric = value.substring(18,22); + String voltage = value.substring(26,30); + String powerValue = Float.valueOf((float)(Integer.valueOf(power,16))/10)+""; + String energyValue = Float.valueOf((float)(Integer.valueOf(energy,16))/100)+""; + String electricValue = Float.valueOf((float)(Integer.valueOf(electric,16))/1000)+""; + String voltageValue = Float.valueOf((float)(Integer.valueOf(voltage,16))/10)+""; + String message = "{\""+deviceId+"_Voltage_485\":"+voltageValue+",\""+deviceId+"_Current_485\":"+electricValue+",\""+deviceId+ + "_Instantaneous_power_485\":"+powerValue+",\""+deviceId+"_Amount_of_charge_485\":"+energyValue+"}"; + messageList.add(message); + } + break; + case 60: + // JAZE温湿度センサー + if(value.startsWith("01")){ + String temperatureValue = Float.valueOf((float) (Integer.valueOf(value.substring(2,6),16)/10.0))+""; + String humidity = Float.valueOf(Integer.valueOf(value.substring(6,8),16))+""; + String message = "{\""+deviceId+"_jzth_temperature\":"+temperatureValue+",\""+deviceId+"_jzth_humidity\":"+humidity+"}"; + messageList.add(message); + } + break; + case 61: + // D485ZT92+M5X-UNIT + if(value.startsWith("0101")){ + String current = Float.valueOf(Integer.valueOf(value.substring(4,12),16))+""; + String voltage = Float.valueOf((float) (Integer.valueOf(value.substring(12,20),16)/100.0))+""; + String amount_of_charge = Float.valueOf((float) (Integer.valueOf(value.substring(20,28),16)/10.0))+""; + String message = "{\""+deviceId+"_m5x_current_485\":"+current+",\""+deviceId+"_m5x_voltage_485\":"+voltage+",\""+deviceId+"_m5x_amount_of_charge_485\":"+amount_of_charge+"}"; + messageList.add(message); + } + break; + case 62: + // D4ACZT92+KS2900 + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + double calValue = ((anaValue-4)/16.0)*(500-0); +// analysisValue = calValue+""; + String message = "{\""+deviceId+"_pressure_difference_ks2900_420\":"+String.format("%.1f", calValue)+"}"; + messageList.add(message); + } + break; + case 54: + // 温度トランスミッター + if(value.startsWith("01")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2),16)/100.0)); + double calValue = (anaValue-4)*9.375; + String message = "{\""+deviceId+"_ttm103A_temp_420\":"+String.format("%.2f", calValue)+"}"; + messageList.add(message); + + } + break; + case 55: + if(value.startsWith("04")){ + float anaValue = Float.valueOf((float) (Integer.valueOf(value.substring(2,4),16)/100.0)); + double calValue = anaValue * 1; + String message = "{\""+deviceId+"_hydraulic\":"+String.format("%.2f", calValue)+"}"; + messageList.add(message); + + } + break; + case 85: + //JAZE漏水センサー + String leakValue3 = ""; + if(value.startsWith("01")){ + if (value.endsWith("01")){ + leakValue3 = "1"; + }else if (value.endsWith("02")){ + leakValue3 = "0"; + }else if (value.equals("0100")){ + leakValue3 = "0"; + } + + }else if(value.startsWith("02")){ + leakValue3 = "1"; + }else if(value.startsWith("03")){ + leakValue3 = "0"; + } + //判断成功解析后发送 + if (!leakValue3.equals("")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"_jzth_leakage\":"+leakValue3+"}"; + messageList.add(message); + } + break; + case 93: + //ドアセンサー-状態 + if(value.startsWith("0a") || (value.startsWith("01") && value.endsWith("01"))){ + String message = "{\""+deviceId+"_delta_status\":"+1+"}"; + messageList.add(message); + }else if(value.startsWith("0b") || (value.startsWith("01") && value.endsWith("00"))){ + String message = "{\""+deviceId+"_delta_status\":"+0+"}"; + messageList.add(message); + } + break; + case 94: + if(value.startsWith("01") && value.endsWith("01")){ + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-doorStatus\":"+1+"}"; + messageList.add(message); + }else if(value.startsWith("01") && value.endsWith("00")) { + String message = "{\"detectorId\":\""+deviceId+"\",\""+deviceId+"-doorStatus\":"+0+"}"; + messageList.add(message); + } + break; + } + return messageList; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/impl/DataProcessServiceImpl.java b/src/main/java/com/techsor/datacenter/sender/service/impl/DataProcessServiceImpl.java new file mode 100644 index 0000000..3738912 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/impl/DataProcessServiceImpl.java @@ -0,0 +1,1624 @@ +package com.techsor.datacenter.sender.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.baidu.fsg.uid.UidGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; +import com.techsor.datacenter.sender.components.CommonOpt; +import com.techsor.datacenter.sender.compiler.JsonPathExtractor; +import com.techsor.datacenter.sender.compiler.MvelExecutor; +import com.techsor.datacenter.sender.components.GuavaRedisCache; +import com.techsor.datacenter.sender.config.DataSourceContextHolder; +import com.techsor.datacenter.sender.constants.Constants; +import com.techsor.datacenter.sender.dao.*; +import com.techsor.datacenter.sender.dto.DeviceAlertInfo; +import com.techsor.datacenter.sender.dto.DeviceInfoVO; +import com.techsor.datacenter.sender.entitiy.*; +import com.techsor.datacenter.sender.entitiy.bastatus.BaStatusEntity; +import com.techsor.datacenter.sender.entitiy.bastatus.BaStatusThirdReqEntity; +import com.techsor.datacenter.sender.entitiy.company.CompanyEntity; +import com.techsor.datacenter.sender.entitiy.delta.DeltaNumEntity; +import com.techsor.datacenter.sender.entitiy.kingio.KingIODbmEntity; +import com.techsor.datacenter.sender.entitiy.st150.St150DataItem; +import com.techsor.datacenter.sender.entitiy.st150.St150DbmEntity; +import com.techsor.datacenter.sender.service.AlarmDataPushLambda; +import com.techsor.datacenter.sender.service.DispatchService; +import com.techsor.datacenter.sender.service.IDataProcessService; +import com.techsor.datacenter.sender.service.IDeviceTypeConfigService; +import com.techsor.datacenter.sender.service.MessagePublisher; +import com.techsor.datacenter.sender.utils.ArithUtil; +import com.techsor.datacenter.sender.utils.DateUtils; +import com.techsor.datacenter.sender.utils.HttpUtil; + +import cn.hutool.core.collection.CollectionUtil; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang.StringUtils; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.MqttPersistenceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.text.MessageFormat; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static org.apache.commons.text.StringEscapeUtils.unescapeJson; + + +/** + * 服务组件,用于处理设备数据和模拟JSON数据,同时操作DynamoDB和Redis。 + *

    + * 本服务实现了 {@link IDataProcessService} 接口,提供了设备数据处理和模拟数据生成的功能。 + * 包含与设备相关的配置信息处理,以及数据转发和告警处理的逻辑。 + */ +@Service +public class DataProcessServiceImpl implements IDataProcessService { + + private static Logger log= LoggerFactory.getLogger(DataProcessServiceImpl.class); + +// @Autowired +// @Qualifier("sysMqttClient") +// private MqttClient sysMqttClient; + + @Value("${category.alarm.deviceTypeIds}") + private List alarmTypeIds; + + @Value("${category.measure.deviceTypeIds}") + private List measureTypeIds; + + @Value("${category.accumulate.deviceTypeIds}") + private List accumulateTypeIds; + + @Value("${category.status.deviceTypeIds}") + private List statusTypeIds; + + // 所有设备类型ID集合 + public static final List ALL_DEVICE_TYPE_IDS = new ArrayList<>(); + + @PostConstruct + public void init() { + ALL_DEVICE_TYPE_IDS.addAll(alarmTypeIds); + ALL_DEVICE_TYPE_IDS.addAll(measureTypeIds); + ALL_DEVICE_TYPE_IDS.addAll(accumulateTypeIds); + ALL_DEVICE_TYPE_IDS.addAll(statusTypeIds); + } + + private static final String REDIS_DASHBOARD_DEVICE_STATUS_KEY = "dashboard_device_status"; + + @Resource + RedisTemplate alramRedisTemplate; + + @Resource + AlarmDataPushLambda alarmDataPushLambda; + + @Resource + private IDeviceTypeConfigService deviceTypeConfigService; + + + @Resource + private DeviceAlertTemplateBindDao deviceAlertTemplateBindDao; + + + @Resource + private DeviceAlertTemplateDao deviceAlertTemplateDao; + @Resource + DeviceDao deviceDao; + + @Resource + private DashboardAlertDao dashboardAlertDao; + + @Resource + private DashboardStatisticsDao dashboardStatisticsDao; + + @Resource + private BaStatusDao baStatusDao; + @Resource + private CompanyInfoDao companyInfoDao; + + @Resource + private GuavaRedisCache guavaRedisCache; + @Resource + private UidGenerator uidGenerator; + + @Resource + private DeviceAlertConfigDao deviceAlertConfigDao; + + @Resource + private DeviceForwardConfigDao deviceForwardConfigDao; + + + @Resource + private RedisTemplate redisTemplate; + + @Resource + MessagePublisher messagePublisher; + + @Resource + DispatchService dispatchService; + + @Autowired + private CommonOpt commonOpt; + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + + //告警alert topic + @Value("${amazon.aws.alert.topic}") + private String awsAlertTopic; + + //告警alert topic + @Value("${amazon.aws.normal.topic}") + private String awsTopic; + + @Value("${business.query.push.info}") + private String businessQueryPushInfo; + + @Value("${roidBaStatus.url}") + private String roidBaStatusUrl; + + @Value("${alarm.email.result.url}") + private String emailResultUrl; + + /** + * 处理给定的设备数据。 + *

    + * 根据设备ID获取设备信息,解析并转换给定的数据内容,生成目标JSON格式数据, + * 并根据设备配置决定是否保存到DynamoDB。 + * + * @param content 需要处理的数据内容 + * @param deviceId 设备ID,用于获取设备配置信息 + * @return 转换后的JSON字符串,如果处理失败则返回空字符串 + */ + @Override + public String processData(String content, String deviceId) { + String processJson = ""; +// log.error("Not Error,For debug:"+deviceId); + String topCompanyId= commonOpt.getTopCompanyId(deviceId); + if (topCompanyId==null || topCompanyId.equals("0")){ + return ""; + } + String companyId = ""; + DataSourceContextHolder.clearCurrentDataSourceKey(); + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + log.info("Use datasource for company:"+topCompanyId); + content= unescapeJson(content); + List deviceEntity=this.deviceDao.getDeviceInfoByDeviceId(deviceId); + this.guavaRedisCache.incrementDailyDeviceIdCount(deviceId); + if (CollectionUtil.isEmpty(deviceEntity)){ + log.error("no deviceEntity is found========================>>>> {},{}",deviceId,content); + return ""; + } + + Long typeId=Long.valueOf(deviceEntity.get(0).getTypeId()); + companyId = deviceEntity.get(0).getCompanyId().toString(); + DeviceConfigInfoEntity deviceTypeConfig = this.deviceTypeConfigService.getDeviceTypeConfigById(typeId); + if (Objects.isNull(deviceTypeConfig)) { + log.error("deviceTypeConfig is null"); + return ""; + } + String expressionMapStrs = deviceTypeConfig.getExpressionMap(); + String expressionVarMap = deviceTypeConfig.getExpressionVariableMap(); + + //key 为新JSON里面的字段,value为具体的表达式 + Map expressionMap = convertToMap(expressionMapStrs); + + //key 为表达式,value为新JSON里面的字段 + Map> expressionVariableMap = convertToVariablMap(expressionVarMap); + + String targetJson = generateNewJson(content, expressionVariableMap, expressionMap, deviceTypeConfig.getTargetJsonFormat()); + + log.debug("targetJson:{}", targetJson); + processJson = targetJson; + Map innerMap=JSON.parseObject(targetJson, Map.class); + if (StringUtils.isNotEmpty(MapUtils.getString(innerMap,"BUILDINRAWDATA",""))){ + processJson=MapUtils.getString(innerMap,"BUILDINRAWDATA"); + } + //Save To DynamoDB + //The normal data stored as one item. But [KINGIOSERVER] is special. + //KINGIOSERVER's data can only be processed as a list. So we need to do special process here to split it. + if (deviceTypeConfig.getId()>=79 && deviceTypeConfig.getId()<=84){ + Type listType = new TypeToken>(){}.getType(); + List KingiOItemList = new Gson().fromJson(processJson,listType); + for (KingIODbmEntity item : KingiOItemList) { + this.save(companyId, item.getContent(),item.getDeviceId(),deviceTypeConfig.getDeviceDataSrc(),item.getTs()); + } + }else if (deviceTypeConfig.getId()==86) { + //Process [発停状態] type + try{ + this.processBAStatus(processJson, deviceId, deviceTypeConfig.getDeviceDataSrc(),companyId); + this.save(companyId, processJson, deviceId, deviceTypeConfig.getDeviceDataSrc(),""); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + + } + //If delta data. extract the ts from [reportedAt] + else if ((deviceTypeConfig.getId()>=46 && deviceTypeConfig.getId()<=48) || (deviceTypeConfig.getId()>=57 && deviceTypeConfig.getId()<=59)){ + String receiveTs =""; + try{ +// RAWEntity rawEntity = new Gson().fromJson(content,RAWEntity.class); +// String rawContent = rawEntity.getContent(); + DeltaNumEntity deltaNumEntity = new Gson().fromJson(unescapeJson(content),DeltaNumEntity.class); + String receiveTsStr = deltaNumEntity.getReportedAt(); + receiveTs = DateUtils.parseToTimestamp(receiveTsStr)+""; + }catch (Exception e){ + log.error("Parse [reportAt] time error:"+e.getMessage(),e); + log.error(content); + } + + this.save(companyId, processJson,deviceId,deviceTypeConfig.getDeviceDataSrc(),receiveTs); + } + else { + this.save(companyId, processJson,deviceId,deviceTypeConfig.getDeviceDataSrc(),""); + } + + return processJson; + } + /** + * 生成模拟的JSON数据。 + *

    + * 基于设备ID和给定的内容,按照设备类型配置生成模拟的JSON数据。 + * + * @param content 原始内容 + * @param deviceId 设备ID + * @return 生成的模拟JSON数据,处理失败时返回空字符串 + */ + @Override + public String mockJsonData(String content, String deviceId) { + String companyId= commonOpt.getTopCompanyId(deviceId); + DataSourceContextHolder.clearCurrentDataSourceKey(); + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+companyId); + List deviceEntity=this.deviceDao.getDeviceInfoByDeviceId(deviceId); + + if (CollectionUtil.isEmpty(deviceEntity)){ + log.error("no deviceEntity is found========================>>>> {},{}",deviceId,content); + return ""; + } + + Long typeId=Long.valueOf(deviceEntity.get(0).getTypeId()); + DeviceConfigInfoEntity deviceTypeConfig = this.deviceTypeConfigService.getDeviceTypeConfigById(typeId); + if (Objects.isNull(deviceTypeConfig)) { + log.error("deviceTypeConfig is null"); + return ""; + } + String expressionMapStrs = deviceTypeConfig.getExpressionMap(); + String expressionVarMap = deviceTypeConfig.getExpressionVariableMap(); + + //key 为新JSON里面的字段,value为具体的表达式 + Map expressionMap = convertToMap(expressionMapStrs); + + //key 为表达式,value为新JSON里面的字段 + Map> expressionVariableMap = convertToVariablMap(expressionVarMap); + + + String targetJson = generateNewJson(content, expressionVariableMap, expressionMap, deviceTypeConfig.getTargetJsonFormat()); + + log.debug("targetJson:{}", targetJson); + + + Map innerMap=JSON.parseObject(content, Map.class); + //特殊处理 BUILDINRAWDATA + if(innerMap.containsKey("BUILDINRAWDATA")){ + String buildInRawData=MapUtils.getString(innerMap,"BUILDINRAWDATA"); + return buildInRawData; + } + return targetJson; + } + + /** + * Use to process ZETA ZAIOT data. + */ + public void processZAIoTData(ResultDeviceIdEntity entity){ + String deviceId = entity.getDeviceId(); + String content = entity.getResult(); + + String topCompanyId= commonOpt.getTopCompanyId(deviceId); + if (topCompanyId==null || topCompanyId.equals("0")){ + return; + } + //Switch database + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + log.info("Use datasource for company:"+topCompanyId); + //Start process + List deviceEntity=this.deviceDao.getDeviceInfoByDeviceId(deviceId); + this.guavaRedisCache.incrementDailyDeviceIdCount(deviceId); + if (CollectionUtil.isEmpty(deviceEntity)){ + log.error("no deviceEntity is found========================>>>> {},{}",deviceId); + return; + } + DeviceEntity deviceItem = deviceEntity.get(deviceEntity.size()-1); + log.debug("Start ZAIot ioserver:"+deviceItem.getDeviceId()); + this.save(deviceEntity.get(0).getCompanyId().toString(), content,deviceId,"zaiot",entity.getTs()); + DataSourceContextHolder.clearCurrentDataSourceKey(); + } + + + /** + * 根据原始JSON、表达式变量映射、表达式映射和目标JSON格式生成新的JSON字符串。 + *

    + * 此方法通过将原始JSON中的值应用到定义的表达式映射中,生成一个新的JSON对象。 + * 这涉及到解析原始JSON、评估表达式,并将结果更新到目标JSON格式中。 + * + * @param originalJson 原始JSON字符串 + * @param expressionVariableMaps 表达式变量映射,定义了如何从原始JSON中提取变量 + * @param expressionMaps 表达式映射,定义了生成新JSON字段的表达式 + * @param targetJsonFormat 目标JSON格式,指定新JSON的结构 + * @return 生成的新JSON字符串 + */ + public String generateNewJson( + String originalJson, + Map> expressionVariableMaps, + Map expressionMaps, + String targetJsonFormat) { + + Configuration configuration = Configuration.builder() + .mappingProvider(new JacksonMappingProvider()) + .build(); + Object originalDocument = configuration.jsonProvider().parse(originalJson); + Map evaluatedMap = evaluateExpressions(originalDocument, expressionVariableMaps); + + Object targetDocument = configuration.jsonProvider().parse(targetJsonFormat); + updateTargetDocument(targetDocument, expressionMaps, evaluatedMap); + + return configuration.jsonProvider().toJson(targetDocument); + } + /** + * 评估原始文档中的表达式,生成一个映射,其中包含每个表达式的计算结果。 + * + * @param originalDocument 原始文档对象 + * @param expressionVariableMaps 表达式变量映射 + * @return 包含表达式计算结果的映射 + */ + private Map evaluateExpressions(Object originalDocument, Map> expressionVariableMaps) { + Map evaluatedMap = new HashMap<>(); + expressionVariableMaps.forEach((expression, variables) -> { + Map mvelMap = generateMvelMap(originalDocument, variables); + String evaluatedExpression = evaluateExpression(expression, mvelMap); + evaluatedMap.put(expression, evaluatedExpression); + }); + return evaluatedMap; + } + /** + * 根据原始文档和变量生成MVEL表达式的映射。 + * + * @param originalDocument 原始文档对象 + * @param variables 变量映射 + * @return 生成的MVEL表达式映射 + */ + private Map generateMvelMap(Object originalDocument, Map variables) { + Map mvelMap = new HashMap<>(); + if (variables.containsKey("$.this")) { + + mvelMap.put("__this", JSON.toJSONString(originalDocument)); + } else { + variables.forEach((k, v) -> { + Queue innerQueue = JsonPathExtractor.infixToRPN(k); + while (!innerQueue.isEmpty()) { + String currentKeyVars = innerQueue.poll(); + if (isJsonPath(currentKeyVars)) { + Object tempValue = JsonPath.parse(originalDocument).read(currentKeyVars); + mvelMap.put(convertKeyVariables(currentKeyVars), tempValue); + } + } + }); + } + return mvelMap; + } + + /** + * 根据提供的MVEL映射评估表达式。 + * + * @param expression 待评估的表达式 + * @param mvelMap MVEL映射 + * @return 表达式的计算结果 + */ + private String evaluateExpression(String expression, Map mvelMap) { + if (expression.contains("$.this")) { + return (String) MvelExecutor.eval(expression.replace("$.this", "__this"), mvelMap); + } else { + return (String) MvelExecutor.eval(convertKeyVariables(expression), mvelMap); + } + } + /** + * 更新目标文档对象,将评估的表达式结果应用到目标JSON中。 + * + * @param targetDocument 目标文档对象 + * @param expressionMaps 表达式映射 + * @param evaluatedMap 包含评估结果的映射 + */ + private void updateTargetDocument(Object targetDocument, Map expressionMaps, Map evaluatedMap) { + for (Map.Entry entry : expressionMaps.entrySet()) { + String path = entry.getKey(); + String expression = entry.getValue(); + if (!evaluatedMap.containsKey(expression)) continue; + Object value = evaluatedMap.get(expression); + if (path.equals("$.this") || path.contains("$.this")) { + JsonPath.parse(targetDocument).put("$", "BUILDINRAWDATA", value); + } else { + JsonPath.parse(targetDocument).put("$", path.replace("$.", ""), value); + } + } + } + + private static boolean isJsonPath(String s) { + return s.startsWith("$"); + } + + private String convertKeyVariables(String jsonPath) { + String result = jsonPath.replaceAll("\\$", "_var"); + String tempResult = result.replaceAll("\\.", "_"); + String tempResultA = tempResult.replaceAll("\\[", "_"); + String tempResultB = tempResultA.replaceAll("\\]", "_"); + String tempResultC = tempResultB.replaceAll("'", "_"); + String tempResultD = tempResultC.replaceAll("-", "_"); + return tempResultD; + } + + /** + * 1. 有转发目标,没有告警目标,存true,存普通表。 + * 2. 没有转发目标,有告警目标,存true,存告警表和普通表。 + * 3. 没有转发目标,没有告警目标,存false,存普通表。 + * 4. 有转发目标,有告警目标,存true,存告警表和普通表。 + * + * **/ + @Override + public void save(String companyId, String content, String deviceId, String code,String receiveTsFromData) { + processSave(companyId, content, deviceId, code, "",receiveTsFromData, this::getUdfCommonInfoByProject); + } + + /** + * 保存特定于Metcom的设备数据到DynamoDB。 + *

    + * 此方法创建一个新的DynamodbEntity对象,用于存储设备ID、源类型、原始数据和其他相关信息。 + * 它还处理了时间戳信息和需要转发的标志。所有这些信息最终被保存到DynamoDB中。 + * + * @param content 设备数据的原始内容。 + * @param deviceId 设备的唯一标识符。 + */ + @Override + public void saveMetcom(String content, String deviceId) { + + DynamodbEntity baseTransDataEntity = new DynamodbEntity(); + baseTransDataEntity.setDeviceId(deviceId); + baseTransDataEntity.setSrcType("METCOM"); + baseTransDataEntity.setRawData(content); + baseTransDataEntity.setStatus("normal"); + baseTransDataEntity.setAlertLevel(0); + baseTransDataEntity.setAlertContent(""); + baseTransDataEntity.setAlertCancelContent(""); + + long snowflakeValue=this.uidGenerator.getUID(); + baseTransDataEntity.setMessageId(String.valueOf(snowflakeValue)); + + baseTransDataEntity.setBuildingInfo(JSON.toJSONString(checkUdfCommonInfo(new UdfCommonJsonInfo()))); + baseTransDataEntity.setFloorInfo(JSON.toJSONString(checkUdfCommonInfo(new UdfCommonJsonInfo()))); + baseTransDataEntity.setSpaceInfo(JSON.toJSONString(checkUdfCommonInfo(new UdfCommonJsonInfo()))); + baseTransDataEntity.setProjectInfo(JSON.toJSONString(checkUdfCommonInfo(new UdfCommonJsonInfo()))); + + //设置类别信息 + baseTransDataEntity.setEquipmentInfo(JSON.toJSONString(checkUdfCommonInfo(new UdfCommonJsonInfo()))); + + baseTransDataEntity.setPlatformIdentifyId(UUID.randomUUID().toString()); + + baseTransDataEntity.setReceive_ts(System.currentTimeMillis()); + baseTransDataEntity.setTargetId(""); + baseTransDataEntity.setProblemReportCategoryId(""); + baseTransDataEntity.setForwardType("api"); + baseTransDataEntity.setBuildingId(""); + baseTransDataEntity.setBuildingCode(""); + baseTransDataEntity.setNeedTransfer("true"); + Map complexMap = DateUtils.getComplexDate(); + baseTransDataEntity.setTs(System.currentTimeMillis() + ""); + baseTransDataEntity.setYearKey(complexMap.get("yearKey")); + baseTransDataEntity.setMonthKey(complexMap.get("monthKey")); + baseTransDataEntity.setDayKey(complexMap.get("dayKey")); + baseTransDataEntity.setDateKey(complexMap.get("dateKey")); + this.messagePublisher.publish(awsTopic, JSON.toJSONString(baseTransDataEntity)); + log.debug("save data to aws mqtt success!! deviceId:{},contents:{}", deviceId, JSON.toJSON(baseTransDataEntity)); + } + /** + * 根据各种条件处理并保存设备数据到DynamoDB。 + *

    + * 此方法验证提供的内容,成功验证后,检索设备配置和警报信息。然后,它创建一个带有相关信息的DynamodbEntity,并根据设备转发关系和警报配置确定数据是否需要转发。 + * 最后,它触发任何警报条件,并将数据保存到DynamoDB。 + *

    + * 如果内容为空或特别标记为"BUILDINRAWDATA",则中止处理。同样,如果没有找到给定设备ID的设备实体,也会中止处理。评估警报条件,并根据结果,在保存前可能更新数据的警报状态。 + * + * @param content 要处理和保存的原始数据内容。 + * @param deviceId 正在为其保存数据的设备的唯一标识符。 + * @param code 表示保存过程的附加元数据或上下文的代码。 + * @param srcType 数据的来源类型,指示其来源或性质。 + * @param udfInfoFunction 一个双参数函数,用于根据给定的字符串(表名)和长整型值(ID)获取通用数据字段(UDF)信息,返回UdfCommonJsonInfo。 + */ + private void processSave(String companyId, String content, String deviceId, String code, String srcType,String receiveTsFromData, BiFunction udfInfoFunction) { + if (StringUtils.isEmpty(content) || content.contains("BUILDINRAWDATA")) { + log.error("content is null {} {}", deviceId, code); + return; + } + + List deviceEntityList = this.deviceDao.getDeviceInfoByDeviceId(deviceId); + if (CollectionUtil.isEmpty(deviceEntityList)) { + log.error("deviceEntityList is null=======> {},{}", content, deviceId); + return; + } + + DeviceEntity currentDevice = deviceEntityList.get(0); + Integer deviceConfigId = currentDevice.getId(); + List currentDeviceAlertInfoList = this.deviceAlertConfigDao.selectCurrentDeviceAlertConfigByDeviceId(Long.valueOf(deviceConfigId)); + List deviceForwardRelations = this.deviceForwardConfigDao.selectDeviceForwardConfigByDeviceId(Long.valueOf(deviceConfigId)); + List alertTemplateIdList=this.deviceAlertTemplateBindDao.selectDeviceAlertTemplateBindByDeviceId(Long.valueOf(currentDevice.getId())); + List currentDeviceAlertInfoBindTemplateLists = this.deviceAlertTemplateDao.selectDeviceAlertTemplatesByDeviceId(Long.valueOf(deviceConfigId)); + + DynamodbEntity baseTransDataEntity = createBaseEntity(content, deviceId,receiveTsFromData , srcType, currentDevice, udfInfoFunction, + currentDeviceAlertInfoBindTemplateLists, + alertTemplateIdList); + log.debug("CompanyId:"+currentDevice.getCompanyId()); + baseTransDataEntity.setCompanyId(currentDevice.getCompanyId()); + setTransferStatus(baseTransDataEntity, deviceForwardRelations, currentDeviceAlertInfoList); + //等待处理 + Set waitingTargetKeySets=new HashSet<>(); + + //设备信息 + DeviceInfoVO deviceInfoVO = alarmDataPushLambda.queryDeviceInfoByDeviceId(deviceId); + try { + log.debug("triggerAlertConditions:{} {} {}", content, JSON.toJSON(baseTransDataEntity), JSON.toJSON(currentDeviceAlertInfoBindTemplateLists)); + triggerAlertConditions(companyId, content, currentDeviceAlertInfoBindTemplateLists, baseTransDataEntity,waitingTargetKeySets,currentDevice.getDeviceSN(), deviceInfoVO); + } catch (Exception e) { + log.error("triggerAlertConditions error:{}", e.getMessage(), e); + } + this.guavaRedisCache.incrementDailyDispatchCount(deviceId); + + if (deviceInfoVO!=null){ + if (deviceInfoVO.getCategory()!=null){ + baseTransDataEntity.setCategory(deviceInfoVO.getCategory()); + } + } + baseTransDataEntity.setHashId(UUID.randomUUID()); + + try { + handleDashboardAlert(baseTransDataEntity); + } catch (Exception e) { + log.error("dashboard alert error", e); + } + + try { + minuteLevelStorage(baseTransDataEntity); + } catch (Exception e) { + log.error("minuteLevelStorage error", e); + } + +// try { +// if ("alert".equals(baseTransDataEntity.getStatus())) { +// sysAlarmNotify(baseTransDataEntity, deviceInfoVO); +// } +// } catch (Exception e) { +// log.error("sysAlarmNotify error", e); +// } + + log.info(" data sent to aws messagePublisher begin!! deviceId:{},contents:{}", deviceId, JSON.toJSON(baseTransDataEntity)); + this.messagePublisher.publish(awsTopic, JSON.toJSONString(baseTransDataEntity)); + +// if (baseTransDataEntity.getNeedTransfer()=="true"){ +// log.debug("dispatch data success!! deviceId:{},contents:{}", deviceId, JSON.toJSON(baseTransDataEntity)); +// String triggerExpression=""; +// if(CollectionUtil.isNotEmpty(currentDeviceAlertInfoList)){ +// triggerExpression=currentDeviceAlertInfoBindTemplateLists.get(0).getContents(); +// } +// this.dispatchService.dispatchMessage(baseTransDataEntity.getDeviceId(), JSON.toJSONString(baseTransDataEntity),triggerExpression,0,waitingTargetKeySets); +// } + log.debug("save data to aws mqtt success!! deviceId:{},contents:{}", deviceId, JSON.toJSON(baseTransDataEntity)); + + //全部转发iotcore + alarmDataPushLambda.iotcoreOpt(companyId, deviceId, JSON.parseObject(JSON.toJSONString(baseTransDataEntity)), deviceInfoVO, 2); + + //更新 + } + + +// private void sysAlarmNotify(DynamodbEntity baseTransDataEntity, DeviceInfoVO deviceInfoVO) throws Exception { +// String temp = "建物「{0}」の監視ポイント「{1}」でアラームが発生しました"; +// String content = MessageFormat.format(temp, deviceInfoVO.getBuildingName(), deviceInfoVO.getMonitoringPointName()); +// MqttMessage message = new MqttMessage(content.getBytes()); +// message.setQos(1); +// String topic = MessageFormat.format("sys/alarmNotify/{0}/{1}", baseTransDataEntity.getCompanyId(), baseTransDataEntity.getDbBuildingId()); +// sysMqttClient.publish(topic, message); +// } + + + @Override + public void minuteLevelStorage(DynamodbEntity baseTransDataEntity) throws Exception { + String uploadValue = extractFirstValue(baseTransDataEntity.getRawData()); + if (StringUtils.isBlank(uploadValue)) { + return; + } + + if (accumulateTypeIds.contains(baseTransDataEntity.getTypeId())) { + storageAccumulate(uploadValue, baseTransDataEntity); + } + + if (measureTypeIds.contains(baseTransDataEntity.getTypeId())) { + storageMeasure(uploadValue, baseTransDataEntity); + } + } + + private void storageMeasure(String uploadValue, DynamodbEntity baseTransDataEntity) throws Exception { + BigDecimal currentValue = new BigDecimal(uploadValue); + BigDecimal minValue = currentValue; + BigDecimal maxValue = currentValue; + + // 获取东京时间 + ComplexTime complexTime = DateUtils.getComplexTime(baseTransDataEntity.getReceive_ts()); + + // 获取 Redis 数据 + String currentDayKey = Constants.STATISTICS_MEASURE_LATEST_PREFIX + complexTime.getDateKey(); + Object currentDayInfoObj = this.redisTemplate.opsForHash().get(currentDayKey, baseTransDataEntity.getDeviceId()); + + //比较值 + if (null != currentDayInfoObj) { + StatisticsMeasureInfo currentDayInfo = objectMapper.readValue(currentDayInfoObj.toString(), StatisticsMeasureInfo.class); + BigDecimal oldMaxValue = new BigDecimal(currentDayInfo.getMaxValue().toString()); + BigDecimal oldMinValue = new BigDecimal(currentDayInfo.getMinValue().toString()); + if (ArithUtil.compareTo(oldMaxValue, currentValue) > 0) { + maxValue = oldMaxValue; + } + if (ArithUtil.compareTo(currentValue, oldMinValue) > 0) { + minValue = oldMinValue; + } + } + + //最新数据存入redis + StatisticsMeasureInfo currentInfo = new StatisticsMeasureInfo(); + BeanUtils.copyProperties(complexTime, currentInfo); + currentInfo.setValue(uploadValue); + currentInfo.setUploadAt(baseTransDataEntity.getReceive_ts()); + currentInfo.setMaxValue(maxValue); + currentInfo.setMinValue(minValue); + + redisTemplate.opsForHash().put(currentDayKey, baseTransDataEntity.getDeviceId(), JSON.toJSONString(currentInfo)); + // 设置过期时间 7 天 + redisTemplate.expire(currentDayKey, 7, TimeUnit.DAYS); + + //历史表和实时表 + dashboardStatisticsDao.insertDeviceMeasureInfo(uploadValue, baseTransDataEntity.getDeviceId(), currentInfo); + dashboardStatisticsDao.upsertDeviceRealtimeMeasure(uploadValue, baseTransDataEntity.getDeviceId(), minValue, maxValue, currentInfo); + + } + + private void storageAccumulate(String uploadValue, DynamodbEntity baseTransDataEntity) throws Exception { + BigDecimal currentValue = new BigDecimal(uploadValue); + Double incrementToday = null; + Double incrementMinute = null; + + // 获取东京时间 + ComplexTime complexTime = DateUtils.getComplexTime(baseTransDataEntity.getReceive_ts()); + + // 获取 Redis 数据 + String currentDayKey = Constants.STATISTICS_ACCUMULATE_LATEST_PREFIX + complexTime.getDateKey(); + String lastDayKey = Constants.STATISTICS_ACCUMULATE_LATEST_PREFIX + complexTime.getPreviousDateKey(); + Object currentDayInfoObj = this.redisTemplate.opsForHash().get(currentDayKey, baseTransDataEntity.getDeviceId()); + Object lastDayInfoObj = this.redisTemplate.opsForHash().get(lastDayKey, baseTransDataEntity.getDeviceId()); + + // 今日增量 + // 如果昨天的没有,那直接取当前数据 + if (null != lastDayInfoObj) { + StatisticsAccumulateInfo lastDayInfo = objectMapper.readValue(lastDayInfoObj.toString(), StatisticsAccumulateInfo.class); + BigDecimal lastDayValue = new BigDecimal(lastDayInfo.getValue().toString()); + if (ArithUtil.compareTo(currentValue, lastDayValue) >= 0) { + incrementToday = ArithUtil.sub(currentValue, lastDayValue); + } + } else { + incrementToday = currentValue.doubleValue(); + } + + // 1分钟增量 + // 这个是如果这条数据的前一分钟没有数据的话,这条数据它就不计算增量,但是保留这个数据,然后下一条数据上来的时候就继续跟这条数据计算增量就行 + long diff = 3600000L; + BigDecimal lastMinuteValue = null; + long nowTs = baseTransDataEntity.getReceive_ts(); + if (currentDayInfoObj == null) { + if (complexTime.getHourKey() == 0 && lastDayInfoObj != null) { + StatisticsAccumulateInfo lastMinInfo = objectMapper.readValue(lastDayInfoObj.toString(), StatisticsAccumulateInfo.class); + if (nowTs - lastMinInfo.getUploadAt() < diff) { //需要1小时内的数据 + lastMinuteValue = new BigDecimal(String.valueOf(lastMinInfo.getValue())); + } + } + } else { + StatisticsAccumulateInfo currentDayInfo = objectMapper.readValue(currentDayInfoObj.toString(), StatisticsAccumulateInfo.class); + if (nowTs - currentDayInfo.getUploadAt() < diff) { + lastMinuteValue = new BigDecimal(String.valueOf(currentDayInfo.getValue())); + } + } + if (lastMinuteValue != null && currentValue.compareTo(lastMinuteValue) >= 0) { + incrementMinute = currentValue.subtract(lastMinuteValue).doubleValue(); + } + + //最新数据存入redis + StatisticsAccumulateInfo currentInfo = new StatisticsAccumulateInfo(); + BeanUtils.copyProperties(complexTime, currentInfo); + currentInfo.setValue(uploadValue); + currentInfo.setUploadAt(baseTransDataEntity.getReceive_ts()); + + redisTemplate.opsForHash().put(currentDayKey, baseTransDataEntity.getDeviceId(), JSON.toJSONString(currentInfo)); + // 设置过期时间 7 天 + redisTemplate.expire(currentDayKey, 7, TimeUnit.DAYS); + + //历史表和日期实时表 + dashboardStatisticsDao.insertDeviceAccumulateInfo(uploadValue, baseTransDataEntity.getDeviceId(), incrementToday, incrementMinute, currentInfo); + dashboardStatisticsDao.insertOrUpdateRealtimeAccumulateDay(uploadValue, baseTransDataEntity.getDeviceId(), incrementToday, currentInfo); + } + + public String extractFirstValue(String rawData) { + if (StringUtils.isBlank(rawData)){ + return ""; + } + try { + JsonNode node = objectMapper.readTree(rawData); + Iterator> fields = node.fields(); + if (fields.hasNext()) { + return fields.next().getValue().asText(); + } + } catch (Exception e) { + log.error("Failed to parse rawData JSON: " + rawData, e); + } + return ""; + } + + + private void handleDashboardAlert(DynamodbEntity baseTransDataEntity) { + + if (!ALL_DEVICE_TYPE_IDS.contains(baseTransDataEntity.getTypeId())) { + return; + } + + Object redisOldStatusObj = this.alramRedisTemplate.opsForHash().get(REDIS_DASHBOARD_DEVICE_STATUS_KEY, baseTransDataEntity.getDeviceId()); + + //更新实时信息 + try { + dashboardAlertDao.upsertDeviceRawData(baseTransDataEntity); + this.alramRedisTemplate.opsForHash().put( + REDIS_DASHBOARD_DEVICE_STATUS_KEY, + baseTransDataEntity.getDeviceId(), + baseTransDataEntity.getStatus()); + } catch (Exception e) { + log.error("upsertDeviceRawData error", e); + } + + //告警历史处理 + if (alarmTypeIds.contains(baseTransDataEntity.getTypeId())) { + String status = baseTransDataEntity.getStatus(); + if (null == redisOldStatusObj) { + if ("alert".equals(status)) { + dashboardAlertDao.insertAlertHistory(baseTransDataEntity); + } + } else { + String redisOldStatus = (String) redisOldStatusObj; + if ("alert".equals(status) && !"alert".equals(redisOldStatus)) { + dashboardAlertDao.insertAlertHistory(baseTransDataEntity); + } else if (!"alert".equals(status) && "alert".equals(redisOldStatus)) { + if (0 == baseTransDataEntity.getRetainAlert()) { + dashboardAlertDao.updateLatestAlertToAutoRecovered(baseTransDataEntity); + } + } + } + } + } + + /** + * 根据设备信息和内容创建一个DynamodbEntity基础实体。 + *

    + * 该方法利用给定的设备信息和内容创建并返回一个DynamodbEntity对象。 + * 它使用udfInfoFunction回调函数来填充UDF相关信息,并设置消息ID和时间戳信息。 + * + * @param content 设备数据的原始内容。 + * @param deviceId 设备的唯一标识符。 + * @param srcType 数据的来源类型。 + * @param currentDevice 当前设备的实体对象。 + * @param udfInfoFunction 用于获取UDF信息的回调函数。 + * @param currentDeviceAlertInfoList 当前设备的警告配置列表。 + * @return 准备好的DynamodbEntity对象。 + */ + private DynamodbEntity createBaseEntity(String content, String deviceId,String receiveTsFromData, String srcType, DeviceEntity currentDevice, BiFunction udfInfoFunction, + List currentDeviceAlertInfoList, + List alertTemplateIdList + ) { + DynamodbEntity baseTransDataEntity = new DynamodbEntity(); + baseTransDataEntity.setDeviceId(deviceId); + baseTransDataEntity.setDbBuildingId(currentDevice.getBuildingId()); + baseTransDataEntity.setRetainAlert(currentDevice.getRetainAlert()); + baseTransDataEntity.setTypeId(currentDevice.getTypeId()); + baseTransDataEntity.setSrcType(srcType); + baseTransDataEntity.setRawData(content); + baseTransDataEntity.setStatus("normal"); + long snowflakeValue=this.uidGenerator.getUID(); + baseTransDataEntity.setMessageId(String.valueOf(snowflakeValue)); + baseTransDataEntity.setBuildingInfo(udfInfoFunction.apply("basic_building", currentDevice.getBuildingId()).getData()); + baseTransDataEntity.setFloorInfo(udfInfoFunction.apply("basic_floor", currentDevice.getFloorId()).getData()); + baseTransDataEntity.setSpaceInfo(udfInfoFunction.apply("basic_space", currentDevice.getSpaceId()).getData()); + baseTransDataEntity.setProjectInfo(udfInfoFunction.apply("basic_project", currentDevice.getProjectId()).getData()); + baseTransDataEntity.setEquipmentInfo(udfInfoFunction.apply("basic_monitoring_asset", currentDevice.getAssetId()).getData()); + baseTransDataEntity.setPlatformIdentifyId(UUID.randomUUID().toString()); + if (ObjectUtils.isEmpty(receiveTsFromData)){ + baseTransDataEntity.setReceive_ts(System.currentTimeMillis()); + }else{ + baseTransDataEntity.setReceive_ts(Long.parseLong(receiveTsFromData)); + } + + this.guavaRedisCache.incrementDailyBuildingCount(String.valueOf(currentDevice.getBuildingId())); + + Map complexMap = DateUtils.getComplexDate(); + baseTransDataEntity.setTs(String.valueOf(System.currentTimeMillis())); + baseTransDataEntity.setYearKey(complexMap.get("yearKey")); + baseTransDataEntity.setMonthKey(complexMap.get("monthKey")); + baseTransDataEntity.setDayKey(complexMap.get("dayKey")); + baseTransDataEntity.setDateKey(complexMap.get("dateKey")); + + + if (CollectionUtil.isNotEmpty(currentDeviceAlertInfoList)) { + baseTransDataEntity.setTargetId(currentDeviceAlertInfoList.get(0).getTargetId()); + baseTransDataEntity.setProblemReportCategoryId(currentDeviceAlertInfoList.get(0).getProblemReportCategoryId()); + baseTransDataEntity.setForwardType(currentDeviceAlertInfoList.get(0).getForwardType()); + List forwardTypeList = currentDeviceAlertInfoList.stream().map(DeviceAlertInfo::getForwardType).collect(Collectors.toList()); + baseTransDataEntity.setForwardTypeList(forwardTypeList); + baseTransDataEntity.setBuildingId(currentDeviceAlertInfoList.get(0).getBuildingId()); + baseTransDataEntity.setBuildingCode(currentDeviceAlertInfoList.get(0).getBuildingCode()); + } else { + baseTransDataEntity.setTargetId(""); + baseTransDataEntity.setProblemReportCategoryId(""); + baseTransDataEntity.setForwardType("roid1"); + baseTransDataEntity.setForwardTypeList(List.of("roid1")); + baseTransDataEntity.setBuildingId(""); + baseTransDataEntity.setBuildingCode(""); + } + if (CollectionUtil.isEmpty(alertTemplateIdList)){ + baseTransDataEntity.setAlertTemplateIds(""); + }else { + baseTransDataEntity.setAlertTemplateIds(JSON.toJSONString(alertTemplateIdList)); + } + + return baseTransDataEntity; + } + + /** + * 设置数据转发状态。 + *

    + * 根据设备转发关系和设备警告配置列表的存在与否,决定是否需要转发数据。 + * 这将影响DynamodbEntity中的"NeedTransfer"字段。 + * + * @param baseTransDataEntity 准备保存的DynamodbEntity对象。 + * @param deviceForwardRelations 设备的转发配置关系列表。 + * @param currentDeviceAlertInfoList 当前设备的警告配置列表。 + */ + private void setTransferStatus(DynamodbEntity baseTransDataEntity, List deviceForwardRelations, List currentDeviceAlertInfoList) { + if (CollectionUtil.isNotEmpty(deviceForwardRelations) && CollectionUtil.isEmpty(currentDeviceAlertInfoList)) { + baseTransDataEntity.setNeedTransfer("true"); + } else if (CollectionUtil.isEmpty(deviceForwardRelations) && CollectionUtil.isNotEmpty(currentDeviceAlertInfoList)) { + baseTransDataEntity.setNeedTransfer("true"); + } else if (CollectionUtil.isEmpty(deviceForwardRelations) && CollectionUtil.isEmpty(currentDeviceAlertInfoList)) { + baseTransDataEntity.setNeedTransfer("false"); + } else if (CollectionUtil.isNotEmpty(deviceForwardRelations) && CollectionUtil.isNotEmpty(currentDeviceAlertInfoList)) { + baseTransDataEntity.setNeedTransfer("true"); + } + } + + + /** + * 触发并处理警告条件。 + *

    + * 该方法检查并触发基于设备警告配置的警告条件。如果存在活动警告或满足警告条件,则更新设备实体的警告状态。 + * 它使用MVEL表达式动态评估设备数据内容以决定警告是否被触发。 + * + * @param content 设备数据的原始内容。 + * @param currentDeviceAlertInfoList 当前设备的警告配置列表。 + * @param baseTransDataEntity 准备保存的DynamodbEntity对象。 + * @param deviceInfoVO + */ + private void triggerAlertConditions(String companyId, String content, + List currentDeviceAlertInfoList, + DynamodbEntity baseTransDataEntity,Set waitingTargetKeySets,String deviceSN, DeviceInfoVO deviceInfoVO) { + log.debug("triggerAlertConditions:{} {} {}", content, JSON.toJSON(baseTransDataEntity), JSON.toJSON(currentDeviceAlertInfoList)); + + if (currentDeviceAlertInfoList.isEmpty()) return; + //先判断当前上一条数据告警是否存在 + Object alertExist = this.redisTemplate.opsForValue().get("alert:alert_alert" + baseTransDataEntity.getDeviceId()); + log.debug("triggerAlertConditions alertExist:"+alertExist); + if (Objects.nonNull(alertExist)){ + boolean isExistingAlert = Boolean.parseBoolean(String.valueOf(alertExist)); + log.debug("triggerAlertConditions isExistingAlert:"+isExistingAlert); + //存在,则清除告警状态 + if (isExistingAlert) { + if (isAlertCleared(content, baseTransDataEntity.getDeviceId(),deviceSN,currentDeviceAlertInfoList.get(0))) { + baseTransDataEntity.setStatus("alert_cancel"); + clearExistingAlert(baseTransDataEntity); + this.guavaRedisCache.incrementDailyAlertCancelCount(baseTransDataEntity.getDeviceId()); + log.debug("triggerAlertConditions set status:"+"alert_cancel"); + } + }else{ + this.guavaRedisCache.incrementDailyAlertCount(baseTransDataEntity.getDeviceId()); + baseTransDataEntity.setStatus("alert"); + log.debug("triggerAlertConditions set status:"+"alert"); + } + }else{ + Map variableMap = new HashMap<>(); + Map mvelMap = extractVariablesAndValues(content,baseTransDataEntity.getDeviceId(),deviceSN, currentDeviceAlertInfoList.get(0), variableMap); + String replacedContents = replaceVariableNames(variableMap, currentDeviceAlertInfoList.get(0).getContents()); + if (variableMap.isEmpty()||mvelMap.isEmpty()){ + log.error("variableMap is empty or mvelMap is empty {}", JSON.toJSON(baseTransDataEntity)); + return; + } + boolean isAlertTriggered = Boolean.parseBoolean(String.valueOf(MvelExecutor.eval(replacedContents, mvelMap))); + if (!isAlertTriggered){ + baseTransDataEntity.setStatus("alert"); + } + } + + + + String targetId=baseTransDataEntity.getTargetId(); + Map paramsMap=new HashMap<>(); + if (StringUtils.isEmpty(targetId)||StringUtils.equals("-1",targetId)){ + + }else{ + paramsMap=JSON.parseObject(targetId, new TypeReference>(){}); + } + + + Set existingTargetIdSets = new HashSet<>(paramsMap.keySet()); + + triggerAndSave(companyId, content, currentDeviceAlertInfoList, + baseTransDataEntity,waitingTargetKeySets,existingTargetIdSets,deviceSN, deviceInfoVO); + } + + private boolean isAlertCleared(String content,String deviceId,String deviceSN, DeviceAlertInfo alertConfig) { + log.debug("isAlertCleared"); + Map variableMap = new HashMap<>(); + Map mvelMap = extractVariablesAndValues(content,deviceId,deviceSN, alertConfig, variableMap); + + if (variableMap.isEmpty()|| mvelMap.isEmpty()){ + log.error("variableMap is empty or mvelMap is empty {}", JSON.toJSON(alertConfig)); + return false; + } + String replacedContents = replaceVariableNames(variableMap, alertConfig.getContents()); + Object result = MvelExecutor.eval(replacedContents, mvelMap); + return Boolean.parseBoolean(String.valueOf(result)); + } + + private void clearExistingAlert(DynamodbEntity baseTransDataEntity) { + String deleteRedisKey="alert:alert_alert" + baseTransDataEntity.getDeviceId(); +// if (this.redisTemplate.hasKey(deleteRedisKey)){ +// this.redisTemplate.delete(deleteRedisKey); +// } + } + + + private Map extractVariablesAndValues(String content, String deviceId, String deviceSN, DeviceAlertInfo alertConfig, Map variableMap) { + Map mvelMap = new HashMap<>(); + Set variableSets = extractVariables(alertConfig.getContents()); + + for (String variable : variableSets) { + processVariable(content, deviceId, deviceSN, variable, mvelMap, variableMap); + } + return mvelMap; + } + + private void processVariable(String content, String deviceId, String deviceSN, String variable, Map mvelMap, Map variableMap) { + try { + extractAndPutVariable(content, variable, mvelMap, variableMap); + } catch (Exception e) { + handleException(variable, deviceId, deviceSN, content, mvelMap, variableMap, e); + } + } + + private void extractAndPutVariable(String content, String variable, Map mvelMap, Map variableMap) { + if (isJsonPath(variable)) { + Object tempValue = JsonPath.parse(content).read(variable); + String tempPath = convertKeyVariables(variable); + mvelMap.put(tempPath, tempValue); + variableMap.put(variable, tempPath); + } + } + + private void handleException(String variable, String deviceId, String deviceSN, String content, Map mvelMap, Map variableMap, Exception e) { + if (StringUtils.isEmpty(deviceSN)) { + log.error("extractVariablesAndValues error: {}", e.getMessage(), e); + return; + } + attemptVariableSubstitution(variable, deviceId, deviceSN, content, mvelMap, variableMap); + } + + private void attemptVariableSubstitution(String variable, String deviceId, String deviceSN, String content, Map mvelMap, Map variableMap) { + if (StringUtils.contains(variable, deviceId)) { + substituteAndExtractVariable(content, variable, deviceId, deviceSN, mvelMap, variableMap); + } + if (StringUtils.contains(variable, deviceSN)) { + substituteAndExtractVariable(content, variable, deviceSN, deviceId, mvelMap, variableMap); + } + } + + private void substituteAndExtractVariable(String content, String variable, String oldString, String newString, Map mvelMap, Map variableMap) { + String replacedKey = StringUtils.replace(variable, oldString, newString); + if (!replacedKey.equals(variable)) { // 确保替换有效,防止重复处理同一个变量 + try { + extractAndPutVariable(content, replacedKey, mvelMap, variableMap); + } catch (Exception e) { + // 替换后的变量依然失败,记录错误并退出 + log.error("Error processing replaced variable '{}': {}", replacedKey, e.getMessage(), e); + } + } + } + private String replaceVariableNames(Map variableMap, String contents) { + for (Map.Entry entry : variableMap.entrySet()) { + contents = contents.replace(entry.getKey(), entry.getValue()); + } + return contents; + } + + + private void triggerAndSave(String companyId, String content, List currentDeviceAlertInfoList, + DynamodbEntity baseTransDataEntity,Set waitingProcessTargetSets, + Set existingTargetSets,String deviceSN, DeviceInfoVO deviceInfoVO) { + + JSONObject alertTemplateInfo = null; + if (StringUtils.isNotEmpty(baseTransDataEntity.getRawData()) && StringUtils.isNotEmpty(baseTransDataEntity.getAlertTemplateIds())) { + JSONObject postJsonObject = new JSONObject(); + postJsonObject.put("deviceId", baseTransDataEntity.getDeviceId()); + postJsonObject.put("status", baseTransDataEntity.getStatus()); + postJsonObject.put("companyId", companyId); + if (StringUtils.isNotEmpty(baseTransDataEntity.getAlertTemplateIds())){ + postJsonObject.put("alarmTmplIds", baseTransDataEntity.getAlertTemplateIds().replace("[", "").replace("]", "")); + } + log.debug("triggerAndSave postJsonObject:"+postJsonObject.toString()); + + //根据告警模板获取转发信息和告警方式 + String resp = HttpUtil.doPost(businessQueryPushInfo, postJsonObject.toString() , null); + log.info("queryByDeviceId result:{}", resp); + if (StringUtils.isNotBlank(resp)) { + alertTemplateInfo = JSONObject.parseObject(resp); + //Process Value replace/用于替换模版中的{Value}字段,这里和下面一段都是做处理,分别对两块数据进行了处理 + JSONArray parsedAlarmInfoListArray = alertTemplateInfo.getJSONObject("data").getJSONArray("parsedAlarmInfoList"); + JSONObject jsonObject = JSON.parseObject(baseTransDataEntity.getRawData(), JSONObject.class); + List valueList = new ArrayList<>(); + for (String key : jsonObject.keySet()) { + valueList.add(jsonObject.get(key)); + } + String pureValue = org.apache.commons.lang3.StringUtils.join(valueList, ","); + for (int k = 0; k < parsedAlarmInfoListArray.size(); k++) { + // 确保元素是 JSONObject 类型(避免 ClassCastException)[[7]] + // 替换每个数组对象中的alertContent中的Value变量为实际值 + if (parsedAlarmInfoListArray.get(k) instanceof JSONObject) { + JSONObject alarmInfo = parsedAlarmInfoListArray.getJSONObject(k); + String alertContent = alarmInfo.getString("alertContent"); + alarmInfo.put("alertContent",alertContent.replace("{Value}",pureValue)); + parsedAlarmInfoListArray.set(k,alarmInfo); + } + } + log.info("parsedAlarmInfoListArray 1:{}", parsedAlarmInfoListArray); + + + //处理转发 + alarmDataPushLambda.handleTargetUrl(alertTemplateInfo, JSON.parseObject(JSON.toJSONString(baseTransDataEntity))); + } + } + + //处理告警内容中的{Value}变量,用实际值替换 + JSONObject jsonObject = JSON.parseObject(baseTransDataEntity.getRawData(), JSONObject.class); + List valueList = new ArrayList<>(); + for (String key : jsonObject.keySet()) { + valueList.add(jsonObject.get(key)); + } + String pureValue = org.apache.commons.lang3.StringUtils.join(valueList, ","); + for (int k = 0; k < currentDeviceAlertInfoList.size(); k++) { + // 替换每个数组对象中的alertContent中的Value变量为实际值 + currentDeviceAlertInfoList.get(k).setMessage(currentDeviceAlertInfoList.get(k).getMessage().replaceAll("\\{Value\\}".toString(),pureValue)); + currentDeviceAlertInfoList.get(k).setAlertCancelMessage(currentDeviceAlertInfoList.get(k).getAlertCancelMessage().replace("\\{Value\\}".toString(),pureValue)); + } + + DeviceAlertInfo currentDeviceAlertInfos = currentDeviceAlertInfoList.get(0); + Map variableMap = new HashMap<>(); + Map mvelMap = extractVariablesAndValues(content,baseTransDataEntity.getDeviceId(),deviceSN, currentDeviceAlertInfos, variableMap); + String replacedContents = replaceVariableNames(variableMap, currentDeviceAlertInfos.getContents()); + + if (variableMap.isEmpty()||mvelMap.isEmpty()){ + log.error("variableMap is empty or mvelMap is empty {}", JSON.toJSON(baseTransDataEntity)); + return; + } + + boolean isAlertTriggered = Boolean.parseBoolean(String.valueOf(MvelExecutor.eval(replacedContents, mvelMap))); + + log.debug("Alert check result: {} for entity: {}", isAlertTriggered, JSON.toJSON(baseTransDataEntity)); + for (String existingTargetSet : existingTargetSets) { + Set keyMvelSets = new HashSet<>(mvelMap.keySet()); + Set finalWaitingProcessTargetSets = waitingProcessTargetSets; + keyMvelSets.stream().filter(key -> { + String afterConvertIterKey = convertKeyVariables(existingTargetSet); + return key.contains(afterConvertIterKey); + }).forEach(key -> finalWaitingProcessTargetSets.add(existingTargetSet)); + } + //在这个范围之内,不告警。不在这个范围之内告警 + //下面是告警,这里判定是相反的,是因为isAlertTriggered判定是的逻辑是告警范围,而现在设定的是正常值范围 + if (!isAlertTriggered) { + //去掉特殊字符 + waitingProcessTargetSets=waitingProcessTargetSets.stream().map(iter-> iter.replaceAll("[\\[\\]'\" ]", "")).collect(Collectors.toSet()); + log.debug("Alert triggered: {}", JSON.toJSON(baseTransDataEntity)); + activateAlert(companyId, currentDeviceAlertInfos, baseTransDataEntity,waitingProcessTargetSets,currentDeviceAlertInfos.getContents(), alertTemplateInfo, deviceInfoVO); + this.guavaRedisCache.incrementDailyAlertCount(baseTransDataEntity.getDeviceId()); + } else { + //去掉特殊字符,判断是否是取消告警 + log.debug("Alert not triggered: {}", JSON.toJSON(baseTransDataEntity)); + if (baseTransDataEntity.getStatus().equals("alert_cancel")){ + activateAlertCancel(companyId, currentDeviceAlertInfos,baseTransDataEntity,waitingProcessTargetSets, alertTemplateInfo, deviceInfoVO); + this.guavaRedisCache.incrementDailyAlertCancelCount(baseTransDataEntity.getDeviceId()); + }else{ + deactivateAlert(baseTransDataEntity); + } + } + } + + private void activateAlert(String companyId, DeviceAlertInfo alertConfig, DynamodbEntity baseTransDataEntity,Set waitingTargetKey,String alertTriggerExpression, JSONObject alertTemplateInfo, DeviceInfoVO deviceInfoVO) { + baseTransDataEntity.setAlertLevel(1); + baseTransDataEntity.setAlertContent(alertConfig.getMessage()); + baseTransDataEntity.setAlertCancelContent(alertConfig.getAlertCancelMessage()); + baseTransDataEntity.setAlertTitle(alertConfig.getAlertTitle()); + baseTransDataEntity.setAlertTypeName(alertConfig.getType()); + baseTransDataEntity.setAlertLevelName(alertConfig.getLevel()); + baseTransDataEntity.setStatus("alert"); + + DynamoAlertDBEntity dynamoAlertDBEntity = new DynamoAlertDBEntity(); + BeanUtils.copyProperties(baseTransDataEntity, dynamoAlertDBEntity); + // this.dynamoDBTemplate.save(dynamoAlertDBEntity); + baseTransDataEntity.setHashId(UUID.randomUUID()); + + // this.messagePublisher.publish(awsAlertTopic, JSON.toJSONString(baseTransDataEntity)); + if (dynamoAlertDBEntity.getNeedTransfer()=="true"){ + this.dispatchService.dispatchMessage(companyId, baseTransDataEntity.getDeviceId(), JSON.toJSONString(baseTransDataEntity),alertTriggerExpression,2,waitingTargetKey, alertTemplateInfo, deviceInfoVO); + } + log.debug("Alert triggered: {}", JSON.toJSON(baseTransDataEntity)); + this.redisTemplate.opsForValue().set("alert:alert_alert" + baseTransDataEntity.getDeviceId(), true); + } + + private void activateAlertCancel(String companyId, DeviceAlertInfo alertConfig, DynamodbEntity baseTransDataEntity,Set waitingTargetKey, JSONObject alertTemplateInfo, DeviceInfoVO deviceInfoVO) { + baseTransDataEntity.setAlertLevel(1); + baseTransDataEntity.setAlertContent(alertConfig.getMessage()); + baseTransDataEntity.setAlertCancelContent(alertConfig.getAlertCancelMessage()); + baseTransDataEntity.setAlertTitle(alertConfig.getAlertCancelTitle()); + baseTransDataEntity.setAlertLevelName(alertConfig.getLevel()); + baseTransDataEntity.setAlertTypeName(alertConfig.getType()); + + DynamoAlertDBEntity dynamoAlertDBEntity = new DynamoAlertDBEntity(); + BeanUtils.copyProperties(baseTransDataEntity, dynamoAlertDBEntity); + + baseTransDataEntity.setHashId(UUID.randomUUID()); + if (dynamoAlertDBEntity.getNeedTransfer()=="true"){ + Gson gson=new Gson(); + this.dispatchService.dispatchMessage(companyId, baseTransDataEntity.getDeviceId(), gson.toJson(baseTransDataEntity),alertConfig.getContents(),0,waitingTargetKey, alertTemplateInfo, deviceInfoVO); + } + } + + private void deactivateAlert(DynamodbEntity baseTransDataEntity) { + log.debug("Alert cleared: {}", JSON.toJSON(baseTransDataEntity)); + baseTransDataEntity.setStatus("normal"); + baseTransDataEntity.setAlertLevel(0); + baseTransDataEntity.setAlertContent(""); + baseTransDataEntity.setAlertCancelContent(""); + } + + + + + + private UdfCommonJsonInfo getUdfCommonInfoByProject(String tableName, Object id){ + UdfCommonJsonInfo udfCommonInfo = new UdfCommonJsonInfo(); + + if (Objects.isNull(id)){ + return checkUdfCommonInfo(udfCommonInfo); + } + + if (id instanceof Long || id instanceof Integer){ + id = id.toString(); + } + + // Ensure id is a string before converting to Long. + if (id instanceof String) { + List resultList = this.deviceDao.getUdfCommonDtoByName(Long.valueOf((String) id), tableName); + if(CollectionUtil.isNotEmpty(resultList)){ + udfCommonInfo = resultList.get(0); + } + } + + return checkUdfCommonInfo(udfCommonInfo); + } + + + + private UdfCommonJsonInfo checkUdfCommonInfo(UdfCommonJsonInfo udfCommonInfo){ + if (StringUtils.isEmpty(udfCommonInfo.getData())){ + udfCommonInfo.setData(""); + } +// if (StringUtils.isEmpty(udfCommonInfo.getName())){ +// udfCommonInfo.setName(""); +// } + return udfCommonInfo; + } + private Map> convertToVariablMap(String jsonMaps) { + Gson gson = new Gson(); + Type type = new TypeToken>>() { + }.getType(); + + Map> map = gson.fromJson(jsonMaps, type); + return map; + } + + + + + private Map convertToMap(String jsonMapStr) { + Gson gson = new Gson(); + Map map = gson.fromJson(jsonMapStr, Map.class); + return map; + } + + + public String getCompanyId(String deviceId){ + String companyId = ""; + try{ + List deviceList = deviceDao.getByDeviceId(deviceId); + if (deviceList.size()!=0){ + companyId = deviceList.get(deviceList.size()-1).getCompanyId().toString(); + }else{ + return ""; + } + + }catch (Exception e){ + log.error("Get companyId Error:",e.getMessage(),e.fillInStackTrace()); + } + log.info("GetCompanyId:"+companyId); + return companyId; + } + + /** + * Process ba-発停状態 + * @param content + * @param deviceId + * @param dataSrcCode + */ + private void processBAStatus(String content, String deviceId, String dataSrcCode,String companyId){ + //Check if have history data + BaStatusEntity historyData = baStatusDao.getByDeviceId(deviceId); + log.debug("ba_status: start process Content: "+content); + Map jsonMap = JSON.parseObject(content, Map.class); + Object valueObj = null; + Integer value = null; + if (!jsonMap.isEmpty()) { + valueObj = jsonMap.values().iterator().next(); + } + // Get Value to Int + if (valueObj == null) { + log.error(String.format("Value cannot be null, content:{}", content)); + return; + } else if (valueObj instanceof BigDecimal) { + BigDecimal bigDecimalValue = (BigDecimal) valueObj; + value = bigDecimalValue.intValue(); + } else if (valueObj instanceof Integer) { + value = (Integer) valueObj; + } else if (valueObj instanceof Long) { + value = ((Long) valueObj).intValue(); // 可能会有数据溢出 + } else if (valueObj instanceof Double) { + value = ((Double) valueObj).intValue(); // 直接取整(去掉小数部分) + } else if (valueObj instanceof String) { + try { + value = Integer.parseInt((String) valueObj); + } catch (NumberFormatException e) { + log.error("Wrong String format,cannot convert to integer: " + valueObj); + log.error(String.format("content:{}", content)); + return; + } + } + else { + log.error("Wrong value format: " + valueObj.getClass()); + log.error(String.format("content:{}", content)); + return; + } + //Get DeviceInfoId by deviceId + DeviceEntity deviceInfo = new DeviceEntity(); + List deviceEntityList = deviceDao.getByDeviceId(deviceId); + if (deviceEntityList.size()>0){ + deviceInfo = deviceEntityList.get(deviceEntityList.size()-1); + }else{ + log.warn("No deviceId exist for ba_status:"+deviceId); + return; + } + //Update Or insert to mysql [ba_status_statistics] + if (historyData==null){ + //insert + BaStatusEntity newData = new BaStatusEntity(); + newData.setContinuousRunningTime(0L); + newData.setAggregatedRunningTime(0L); + newData.setLatest_ts(System.currentTimeMillis()+""); + newData.setDeviceInfoId(deviceInfo.getId()); + if (value==1){ + //Process [start] + newData.setRunningCount(1); + newData.setIsRunning(1); + newData.setLastStartTime(System.currentTimeMillis()); + }else if (value==0){ + //Process [stop] + newData.setRunningCount(0); + newData.setIsRunning(0); + newData.setLastStopTime(System.currentTimeMillis()); + }else{ + log.warn("Not correct Value for bs_status:"+value); + return; + } + baStatusDao.insert(newData); + sendBAStatus2Third(companyId,deviceId,newData); + }else{ + //update + BaStatusEntity updateData = new BaStatusEntity(); + updateData.setId(historyData.getId()); + updateData.setLatest_ts(System.currentTimeMillis()+""); + updateData.setDeviceInfoId(deviceInfo.getId()); + if (value==1){ + //Process [start] + updateData.setIsRunning(1); + updateData.setRunningCount(historyData.getRunningCount()+1); + updateData.setLastStartTime(System.currentTimeMillis()); + updateData.setLastStopTime(historyData.getLastStopTime()); + //check history data's status + if (historyData.getIsRunning()==0){ + log.info("bs_status: process [start],last status [stop]"); + updateData.setAggregatedRunningTime(historyData.getAggregatedRunningTime()); + updateData.setContinuousRunningTime(0L); + }else if (historyData.getIsRunning()==1){ + log.info("bs_status: process [start],last status [start]"); + Long tsPeriod = (System.currentTimeMillis()-Long.parseLong(historyData.getLatest_ts())); + log.info("tsPeriod: "+tsPeriod); + updateData.setAggregatedRunningTime(historyData.getAggregatedRunningTime()+tsPeriod); + updateData.setContinuousRunningTime(0L); + } + }else if (value==0){ + //Process [stop] + updateData.setIsRunning(0); + updateData.setRunningCount(historyData.getRunningCount()); + updateData.setLastStartTime(historyData.getLastStartTime()); + updateData.setLastStopTime(System.currentTimeMillis()); + //check history data's status + if (historyData.getIsRunning()==0){ + log.warn("Break due to last bs_status is stop, the new bs_status is:"+value); + return; + }else if (historyData.getIsRunning()==1){ + log.info("bs_status: process [stop],last status [start]"); + Long tsPeriod = (System.currentTimeMillis()-Long.parseLong(historyData.getLatest_ts())); + log.info("tsPeriod: "+tsPeriod); + updateData.setAggregatedRunningTime(historyData.getAggregatedRunningTime()+tsPeriod); + updateData.setContinuousRunningTime(tsPeriod); + } + }else{ + log.warn("Not correct Value for bs_status:"+value); + return; + } + baStatusDao.update(updateData); + sendBAStatus2Third(companyId,deviceId,updateData); + } + } + + private void sendBAStatus2Third(String companyId,String deviceId,BaStatusEntity baStatusEntity){ + BaStatusThirdReqEntity requestEntity = new BaStatusThirdReqEntity(); + requestEntity.setIs_running(baStatusEntity.getIsRunning()); + requestEntity.setOccured_at(DateUtils.convertTimestampToFormattedDateTime(baStatusEntity.getLatest_ts(),"yyyy-MM-dd HH:mm:ss")); + requestEntity.setContinuous_running_time(DateUtils.convertMillisToHourMinuteFormat(baStatusEntity.getContinuousRunningTime())); + requestEntity.setRunning_count(baStatusEntity.getRunningCount()); + requestEntity.setAggregated_running_time(DateUtils.convertMillisToHourMinuteFormat(baStatusEntity.getAggregatedRunningTime())); + List deviceAlertConfigEntities = deviceAlertConfigDao.selectCurrentDeviceAlertConfigByDeviceId((long)baStatusEntity.getDeviceInfoId()); + if (deviceAlertConfigEntities.size()==0){ + log.warn("This BA status device has no targetId. skip:"+baStatusEntity.getDeviceInfoId()); + return; + }else{ + String rawTargetId = deviceAlertConfigEntities.get(0).getTargetId(); + Map targetIdMap = JSON.parseObject(rawTargetId, new TypeReference>() {}); + if (targetIdMap.isEmpty()) { + log.warn("This BA status device's targetIdMap has no targetId. skip:"+baStatusEntity.getDeviceInfoId()); + return; + } + String targetId = ""; + for (String key : targetIdMap.keySet()) { + targetId = targetIdMap.get(key).toString(); + } + requestEntity.setTargetId(Integer.parseInt(targetId)); + } + // Get apiHost + CompanyEntity companyEntity = companyInfoDao.getCompanyById(companyId); + //Send + sendBAStatus(companyId,deviceId,companyEntity.getThirdApiHost(),companyEntity.getBearerToken(),requestEntity); + } + + private void sendBAStatus(String companyId,String deviceId,String host,String token,BaStatusThirdReqEntity requestEntity){ + if (null == requestEntity.getTargetId()){ + log.error("BA Status skip. No targetId:"); + } + + String url = host+roidBaStatusUrl; + url = url.replace("{targetId}",requestEntity.getTargetId()+""); + HashMap headerMap = new HashMap<>(); + headerMap.put("Content-Type", "application/json;charset=UTF-8"); + headerMap.put("access-control-allow-credentials", "True"); + headerMap.put("X-Requested-With", "XMLHttpRequest"); + headerMap.put("Authorization", token); + headerMap.put("Accept", "application/json, text/plain, /"); + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("is_running",requestEntity.getIs_running()); + jsonObject.put("target_id", requestEntity.getTargetId()); + jsonObject.put("occurred_at", requestEntity.getOccured_at()); + jsonObject.put("continuous_running_time", requestEntity.getContinuous_running_time()); + jsonObject.put("aggregated_running_time", requestEntity.getAggregated_running_time()); + jsonObject.put("running_count", requestEntity.getRunning_count()); + log.info("roid BA status api request : {}", jsonObject.toString()); + HttpResponse httpResponse = HttpUtil.doPostWithRespone(url, jsonObject.toString(), headerMap); + log.info("roid BA status api response : {}", JSON.toJSONString(httpResponse)); + try{ + logRoidResult(companyId,deviceId, httpResponse, url, jsonObject.toString(), "roidBaStatus"); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + } + + + // 更新的正则表达式,能够处理更复杂的嵌套模式 +// private static final Pattern JSON_PATH_PATTERN = Pattern.compile("\\$\\.(?:[a-zA-Z_0-9]+(?:\\[\\d+\\])*(?:\\.[a-zA-Z_0-9]+(?:\\[\\d+\\])*)*)" +// ); + private static final Pattern JSON_PATH_PATTERN = Pattern.compile( + "\\$\\.(?:[a-zA-Z_0-9]+(?:\\[\\d+\\]|\\['[a-zA-Z_0-9\\-_]+'\\])*(?:\\.[a-zA-Z_0-9]+(?:\\[\\d+\\]|\\['[a-zA-Z_0-9\\-_]+'\\])*)*)|\\$\\['[a-zA-Z_0-9\\-_]+'\\]" + ); + public static Set extractVariables(String expression) { + if (StringUtils.isEmpty(expression)){ + return new HashSet<>(); + } + Matcher matcher = JSON_PATH_PATTERN.matcher(expression); + Set variables = new HashSet<>(); + + while (matcher.find()) { + variables.add(matcher.group()); + } + + return variables; + } + + private void logRoidResult(String companyId,String deviceId, HttpResponse httpResponse, String url, String body, String logType) { + //统计邮件成功/失败数量 + JSONObject postJsonObject = new JSONObject(); + postJsonObject.put("companyId", companyId); + postJsonObject.put("errorMsg", httpResponse.getMsg()); + postJsonObject.put("deviceId", deviceId); + postJsonObject.put("logType", logType); + + JSONObject detail = new JSONObject(); + detail.put("content", body); + detail.put("url", url); + postJsonObject.put("detail", detail.toString()); + + if (200 == httpResponse.getCode()) { + postJsonObject.put("status", "Succeeded"); + } else { + postJsonObject.put("status", "Failed"); + } + String resp = HttpUtil.doPost(emailResultUrl, postJsonObject.toString() , null); + log.info("log roid result: {}", resp); + } + + /** + * Use to process king ioserver data. + */ + public void processKingIOServerData(String processJson){ + Type listType = new TypeToken>(){}.getType(); + List KingiOItemList = new Gson().fromJson(processJson,listType); + for (KingIODbmEntity item : KingiOItemList) { + try{ + String topCompanyId= commonOpt.getTopCompanyId(item.getDeviceId()); + if (topCompanyId==null || topCompanyId.equals("0")){ + continue; + } + //Switch database + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + log.info("Use datasource for company:"+topCompanyId); + //Start process + List deviceEntity=this.deviceDao.getDeviceInfoByDeviceId(item.getDeviceId()); + this.guavaRedisCache.incrementDailyDeviceIdCount(item.getDeviceId()); + if (CollectionUtil.isEmpty(deviceEntity)){ + log.error("no deviceEntity is found========================>>>> {},{}",item.getDeviceId()); + continue; + } + DeviceEntity deviceItem = deviceEntity.get(deviceEntity.size()-1); + log.debug("Start process ioserver:"+item.getDeviceId()); + + if (deviceItem.getTypeId()==113) { + //Process [発停状態] type + try { + this.processBAStatus(item.getContent(), deviceItem.getDeviceId(), "", deviceEntity.get(0).getCompanyId().toString()); + } catch (Exception e) { + log.error(e.getMessage(), e); + e.printStackTrace(); + } + } + this.save(deviceEntity.get(0).getCompanyId().toString(), item.getContent(),item.getDeviceId(),"ioserver",item.getTs()); + DataSourceContextHolder.clearCurrentDataSourceKey(); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + + } + } + + /** + * Use to process processSt150Data data. + */ + public void processSt150Data(String processJson){ + Type listType = new TypeToken>(){}.getType(); + List st150DbmEntityList = new Gson().fromJson(processJson,listType); + for (St150DbmEntity item : st150DbmEntityList) { + try{ + String topCompanyId= commonOpt.getTopCompanyId(item.getDeviceId()); + if (topCompanyId==null || topCompanyId.equals("0")){ + continue; + } + //Switch database + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); + log.info("Use datasource for company:"+topCompanyId); + //Start process + List deviceEntity=this.deviceDao.getDeviceInfoByDeviceId(item.getDeviceId()); + this.guavaRedisCache.incrementDailyDeviceIdCount(item.getDeviceId()); + if (CollectionUtil.isEmpty(deviceEntity)){ + log.error("no deviceEntity is found========================>>>> {},{}",item.getDeviceId()); + continue; + } + DeviceEntity deviceItem = deviceEntity.get(deviceEntity.size()-1); + log.debug("Start process st150:"+item.getDeviceId()); + + if (deviceItem.getTypeId()==123) { + //Process [発停状態] type + try { + this.processBAStatus(item.getContent(), deviceItem.getDeviceId(), "", deviceEntity.get(0).getCompanyId().toString()); + } catch (Exception e) { + log.error(e.getMessage(), e); + e.printStackTrace(); + } + } + this.save(deviceEntity.get(0).getCompanyId().toString(), item.getContent(),item.getDeviceId(),"ioserver",item.getTs()); + DataSourceContextHolder.clearCurrentDataSourceKey(); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + + } + } +} + + diff --git a/src/main/java/com/techsor/datacenter/sender/service/impl/DeviceTypeConfigServiceImpl.java b/src/main/java/com/techsor/datacenter/sender/service/impl/DeviceTypeConfigServiceImpl.java new file mode 100644 index 0000000..991f32f --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/impl/DeviceTypeConfigServiceImpl.java @@ -0,0 +1,62 @@ +package com.techsor.datacenter.sender.service.impl; + +import com.techsor.datacenter.sender.dao.DeviceConfigInfoDao; +import com.techsor.datacenter.sender.entitiy.DeviceConfigInfoEntity; +import com.techsor.datacenter.sender.exceptions.BusinessException; +import com.techsor.datacenter.sender.service.IDeviceTypeConfigService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.Objects; + + +@Slf4j +@Service +public class DeviceTypeConfigServiceImpl implements IDeviceTypeConfigService { + + @Resource + private DeviceConfigInfoDao deviceConfigInfoDao; + + @Override + public DeviceConfigInfoEntity getDeviceTypeConfig(String deviceId, Long deviceTypeId) { + if (StringUtils.isEmpty(deviceId) || deviceTypeId == null) { + log.error("deviceId or deviceTypeId is null"); + throw new BusinessException("deviceId or deviceTypeId is null"); + } + DeviceConfigInfoEntity deviceConfigInfoEntity = deviceConfigInfoDao.getDeviceConfigInfo(deviceId, deviceTypeId); + if (Objects.isNull(deviceConfigInfoEntity)){ + throw new BusinessException("deviceConfigInfoEntity is null "+deviceId+" "+deviceTypeId); + } + return deviceConfigInfoEntity; + } + + @Override + public DeviceConfigInfoEntity getDeviceTypeConfigByCode(String code) throws BusinessException { + if (StringUtils.isEmpty(code)) { + log.error("deviceId or deviceTypeId is null"); + throw new BusinessException("deviceId or deviceTypeId is null"); + } + DeviceConfigInfoEntity deviceConfigInfoEntity = deviceConfigInfoDao.getDeviceConfigInfo( code); + if (Objects.isNull(deviceConfigInfoEntity)){ + throw new BusinessException("deviceConfigInfoEntity is null "+code); + } + return deviceConfigInfoEntity; + } + + @Override + public DeviceConfigInfoEntity getDeviceTypeConfigById(Long typeId) throws BusinessException { + if (Objects.isNull(typeId)) { + log.error("deviceId or deviceTypeId is null"); + throw new BusinessException("deviceId or deviceTypeId is null"); + } + DeviceConfigInfoEntity deviceConfigInfoEntity = deviceConfigInfoDao.getDeviceConfigInfoById( typeId); + if (Objects.isNull(deviceConfigInfoEntity)){ + throw new BusinessException("deviceConfigInfoEntity is null "+typeId); + } + return deviceConfigInfoEntity; + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/impl/KinesisMessageService.java b/src/main/java/com/techsor/datacenter/sender/service/impl/KinesisMessageService.java new file mode 100644 index 0000000..e3460a8 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/impl/KinesisMessageService.java @@ -0,0 +1,194 @@ +package com.techsor.datacenter.sender.service.impl; + + +import com.amazonaws.services.kinesis.AmazonKinesis; +import com.amazonaws.services.kinesis.model.PutRecordsRequest; +import com.amazonaws.services.kinesis.model.PutRecordsRequestEntry; +import com.amazonaws.services.kinesis.model.PutRecordsResult; +import com.techsor.datacenter.sender.service.MessagePublisher; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Resource; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.*; + + +@Slf4j +@Service +public class KinesisMessageService implements MessagePublisher { + + @Resource + private AmazonKinesis amazonKinesis; + + + @Value("${amazon.aws.normal.topic}") + private String awsTopic; + + private ScheduledExecutorService scheduledExecutor; + + + private Queue dataList; + private Queue retryRequests; + private static final int MAX_RETRY_ATTEMPTS = 5; + private Map retryAttempts; + + private static List randomList = new ArrayList<>(); + + static { + randomList.add("kinesis-"+0); + randomList.add("kinesis-"+1); + randomList.add("kinesis-"+2); + randomList.add("kinesis-"+3); + randomList.add("kinesis-"+4); + randomList.add("kinesis-"+5); + randomList.add("kinesis-"+6); + randomList.add("kinesis-"+7); + randomList.add("kinesis-"+8); + randomList.add("kinesis-"+9); + randomList.add("kinesis-"+10); + } + @Override + public void publish(String topic, String message) { + if (StringUtils.isEmpty(topic) || StringUtils.isEmpty(message)) { + log.error("Topic or message is empty"); + return; + } + dataList.offer(message); // 使用 offer 来添加消息到队列 + + sendMessages(topic); + } + + @PostConstruct + public void postConstruct() { + this.dataList = new ConcurrentLinkedQueue<>(); + this.retryAttempts= new ConcurrentHashMap<>(); + this.retryRequests = new ConcurrentLinkedQueue<>(); + scheduledExecutor = Executors.newScheduledThreadPool(2); + + // 处理dataList的任务,每3秒执行一次 + scheduledExecutor.scheduleWithFixedDelay(this::processDataList, 0, 3, TimeUnit.SECONDS); + + // 处理retryRequests的任务,每5秒执行一次 + scheduledExecutor.scheduleWithFixedDelay(this::processRetryRequests, 0, 5, TimeUnit.SECONDS); + + } + + private void processDataList() { + String requestId=UUID.randomUUID().toString(); + MDC.put("requestId", requestId); + log.debug("clear dataListBuffer,dataListBufferSize:{}", dataList.size()); + if (!dataList.isEmpty()) { + sendMessages(awsTopic); + } + MDC.clear(); + } + private void processRetryRequests() { + String requestId=UUID.randomUUID().toString(); + MDC.put("requestId", requestId); + log.debug("resend retry message :{}", retryRequests.size()); + PutRecordsRequest putRecordsRequest; + //采用无锁设计 + while ((putRecordsRequest = retryRequests.poll()) != null) { + this.retryFailedMessage(putRecordsRequest); + } + MDC.clear(); + } + + + + private void retryFailedMessage(PutRecordsRequest request) { + log.debug("try again================>"); + Integer attempts = retryAttempts.getOrDefault(request, 0); + if (attempts >= MAX_RETRY_ATTEMPTS) { + log.error("Reached max retry attempts for discarding it. {}",request.getRecords()); + retryAttempts.remove(request); + return; + } + + // Reset the data buffer position + request.getRecords().forEach(entry -> entry.getData().rewind()); + + try { + PutRecordsResult result = amazonKinesis.putRecords(request); + handlePutRecordsResult(result, request); + retryAttempts.remove(request); + log.info("retry successfully!!!"); + } catch (Exception e) { + log.info("Error retrying PutRecordsRequest: {}", e.getMessage()); + retryAttempts.put(request, attempts + 1); // Update attempt count + // 重新进入队列 + retryRequests.offer(request); + } + } + + @PreDestroy + public void preDestroy() { + scheduledExecutor.shutdown(); + try { + // 等待现有任务完成执行 + if (!scheduledExecutor.awaitTermination(60, TimeUnit.SECONDS)) { + // 超时后停止所有正在执行的任务 + scheduledExecutor.shutdownNow(); + + // 再次等待,确保所有任务都已被停止 + if (!scheduledExecutor.awaitTermination(60, TimeUnit.SECONDS)) { + log.error("ScheduledExecutorService did not terminate"); + } + } + } catch (InterruptedException ie) { + // 如果等待过程中线程被中断,重新尝试关闭执行器,并设置当前线程的中断状态 + scheduledExecutor.shutdownNow(); + // 保留中断状态 + Thread.currentThread().interrupt(); + } + } + + + + private void sendMessages(String topic) { + Queue recordsRequestEntries = new ConcurrentLinkedQueue<>(); + String partitionKey = "kinesis-"+((int) (Math.random() * 300000)); + //改为无锁设计,每次限制300条 + while (!dataList.isEmpty() && recordsRequestEntries.size() <= 300) { + String dataString = dataList.poll(); + if (dataString != null) { + ByteBuffer data = ByteBuffer.wrap(dataString.getBytes(StandardCharsets.UTF_8)); + PutRecordsRequestEntry entry = new PutRecordsRequestEntry() + .withData(data) + .withPartitionKey(partitionKey); + recordsRequestEntries.offer(entry); + } + } + if (!recordsRequestEntries.isEmpty()) { + PutRecordsRequest putRecordsRequest = new PutRecordsRequest() + .withRecords(recordsRequestEntries) + .withStreamName(topic); + PutRecordsResult putRecordsResult = null; + try { + putRecordsResult = amazonKinesis.putRecords(putRecordsRequest); + handlePutRecordsResult(putRecordsResult, putRecordsRequest); + } catch (Exception e) { + log.error("Discard recordData result: {}", putRecordsResult); + //去掉synchronized,采用无锁设计 + retryRequests.offer(putRecordsRequest); + + } + + } + } + + private void handlePutRecordsResult(PutRecordsResult result, PutRecordsRequest request) { + if (result.getFailedRecordCount() > 0) { + log.debug("push Failed result, try to put in the retryrequestList: {}", result); + retryRequests.offer(request); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/techsor/datacenter/sender/service/impl/WorkerNodeEntityServiceImpl.java b/src/main/java/com/techsor/datacenter/sender/service/impl/WorkerNodeEntityServiceImpl.java new file mode 100644 index 0000000..5a5ed69 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/service/impl/WorkerNodeEntityServiceImpl.java @@ -0,0 +1,50 @@ +package com.techsor.datacenter.sender.service.impl; + +import com.baidu.fsg.uid.worker.entity.WorkerNodeEntity; +import com.baidu.fsg.uid.worker.service.WorkerNodeEntityService; +import com.techsor.datacenter.sender.config.DataSourceContextHolder; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.PreparedStatementCreator; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; + +@Component +public class WorkerNodeEntityServiceImpl implements WorkerNodeEntityService { + + @Resource + private JdbcTemplate jdbcTemplate; + + @Override + public Long save(WorkerNodeEntity workerNodeEntity) { + + DataSourceContextHolder.clearCurrentDataSourceKey(); + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_0"); + KeyHolder keyHolder = new GeneratedKeyHolder(); + + this.jdbcTemplate.update( + connection -> { + PreparedStatement ps = connection.prepareStatement( + "INSERT INTO worker_node (host_name, port, type, launch_date, modified, created) VALUES (?, ?, ?, ?, ?, ?)", + Statement.RETURN_GENERATED_KEYS); + ps.setString(1, workerNodeEntity.getHostName()); + ps.setString(2, workerNodeEntity.getPort()); + ps.setInt(3, workerNodeEntity.getType()); + ps.setDate(4, java.sql.Date.valueOf(workerNodeEntity.getLaunchDate())); + ps.setTimestamp(5, java.sql.Timestamp.valueOf(workerNodeEntity.getModified())); + ps.setTimestamp(6, java.sql.Timestamp.valueOf(workerNodeEntity.getCreated())); + return ps; + }, + keyHolder); + + Number key = keyHolder.getKey(); + + return key.longValue(); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/utils/ArithUtil.java b/src/main/java/com/techsor/datacenter/sender/utils/ArithUtil.java new file mode 100644 index 0000000..c774434 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/utils/ArithUtil.java @@ -0,0 +1,270 @@ +package com.techsor.datacenter.sender.utils; + +import java.math.BigDecimal; + +/** + * 工具类 - 运算 + */ +public class ArithUtil { + + // 默认除法运算精度 + private static final int DEF_DIV_SCALE = 10; + + private ArithUtil() { + } + + /** + * 提供精确的加法运算。 + * + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(Double v1, Double v2) { + BigDecimal b1 = BigDecimal.valueOf(v1 == null ? 0D : v1); + BigDecimal b2 = BigDecimal.valueOf(v2 == null ? 0D : v2); + return b1.add(b2).doubleValue(); + } + + /** + * 多个值相加 + * + * @param vs + * @return + */ + public static double add(Double... vs) { + BigDecimal b1 = BigDecimal.ZERO; + for (Double d : vs) { + BigDecimal b2 = BigDecimal.valueOf(d == null ? 0D : d); + b1 = b1.add(b2); + } + return b1.doubleValue(); + } + + /** + * 提供精确的减法运算。 + * + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) { + BigDecimal b1 = BigDecimal.valueOf(v1); + BigDecimal b2 = BigDecimal.valueOf(v2); + return b1.subtract(b2).doubleValue(); + } + + public static double sub(BigDecimal v1, BigDecimal v2) { + return v1.subtract(v2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) { + BigDecimal b1 = BigDecimal.valueOf(v1); + BigDecimal b2 = BigDecimal.valueOf(v2); + return b1.multiply(b2).doubleValue(); + } + + /** + * 多个值相乘 + * + * @param values + * @return + */ + public static double mul(Double... values) { + BigDecimal result = BigDecimal.ONE; + for (Double value : values) { + BigDecimal v = BigDecimal.valueOf(value == null ? 0D : value); + result = result.multiply(v); + } + return result.doubleValue(); + } + + /** + * 提供精确的乘法运算,并对结果四舍五入。 + * + * @param v1 被乘数 + * @param v2 乘数 + * @param scale 表示需要精确到小数点以后几位 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2, int scale) { + BigDecimal b1 = BigDecimal.valueOf(v1); + BigDecimal b2 = BigDecimal.valueOf(v2); + return round(b1.multiply(b2).doubleValue(), scale); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数 + * 指定精度,以后的数字四舍五入。 + * + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示需要精确到小数点以后几位 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) { + if (scale < 0) { + throw new IllegalArgumentException("The scale must be a positive integer or zero"); + } + BigDecimal b1 = BigDecimal.valueOf(v1); + BigDecimal b2 = BigDecimal.valueOf(v2); + return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) { + if (scale < 0) { + throw new IllegalArgumentException("The scale must be a positive integer or zero"); + } + BigDecimal b = BigDecimal.valueOf(v); + return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + /** + * double转float + * + * @param v + * @return + */ + public static float convertsToFloat(double v) { + BigDecimal b = BigDecimal.valueOf(v); + return b.floatValue(); + } + + /** + * double转int 不进行四舍五入 + * + * @param v + * @return + */ + public static int convertsToInt(double v) { + BigDecimal b = BigDecimal.valueOf(v); + return b.intValue(); + } + + /** + * double转long + * + * @param v + * @return + */ + public static long convertsToLong(double v) { + BigDecimal b = BigDecimal.valueOf(v); + return b.longValue(); + } + + /** + * 返回两个数中大的一个值 + * + * @param v1 + * @param v2 + * @return + */ + public static double returnMax(double v1, double v2) { + BigDecimal b1 = BigDecimal.valueOf(v1); + BigDecimal b2 = BigDecimal.valueOf(v2); + return b1.max(b2).doubleValue(); + } + + /** + * 返回两个数中小的一个值 + * + * @param v1 + * @param v2 + * @return + */ + public static double returnMin(double v1, double v2) { + BigDecimal b1 = BigDecimal.valueOf(v1); + BigDecimal b2 = BigDecimal.valueOf(v2); + return b1.min(b2).doubleValue(); + } + + /** + * 比较两个数大小 + * + * @param v1 + * @param v2 + * @return -1 表示v1小于v2,0表示相等,1表示v1大于v2 + */ + public static int compareTo(double v1, double v2) { + BigDecimal b1 = BigDecimal.valueOf(v1); + BigDecimal b2 = BigDecimal.valueOf(v2); + return b1.compareTo(b2); + } + + public static int compareTo(BigDecimal v1, BigDecimal v2) { + return v1.compareTo(v2); + } + + /** + * 获取double类型的小数位数 + * + * @param number + * @return + */ + public static int getDecimals(double number) { + String text = Double.toString(number); + int index = text.indexOf("."); + if (index < 0) { + return 0; + } + return text.length() - index - 1; + } + + /** + * 获取float类型的小数位数 + * + * @param number + * @return + */ + public static int getDecimals(float number) { + String text = Float.toString(number); + int index = text.indexOf("."); + if (index < 0) { + return 0; + } + return text.length() - index - 1; + } + + /** + * 精确控制四舍五入策略 + * + * @param value + * @param scale + * @param roundingMode + * @return + */ + public static double round(double value, int scale, int roundingMode) { + BigDecimal bd = BigDecimal.valueOf(value); + return bd.setScale(scale, roundingMode).doubleValue(); + } + + public static void main(String[] args) { + System.out.println(getDecimals(4.574751)); + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/utils/DBMUtils.java b/src/main/java/com/techsor/datacenter/sender/utils/DBMUtils.java new file mode 100644 index 0000000..86994f1 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/utils/DBMUtils.java @@ -0,0 +1,9 @@ +package com.techsor.datacenter.sender.utils; + +public class DBMUtils { + + //DBM标准化值传递格式生成 + public static String generateEventCode(String deviceId, String value){ + return "{\""+deviceId+"\":"+value+"}"; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/utils/DataUtils.java b/src/main/java/com/techsor/datacenter/sender/utils/DataUtils.java new file mode 100644 index 0000000..9014ba9 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/utils/DataUtils.java @@ -0,0 +1,111 @@ +package com.techsor.datacenter.sender.utils; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; +import com.techsor.datacenter.sender.dto.VariableBoundsDTO; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Slf4j +public class DataUtils { + + // Your extractMinMax method adjusted: + + public static VariableBoundsDTO extractMinMax(String expression) { + Pattern pattern = Pattern.compile("\\$\\['(.*?)'\\]\\s*(>=|<=|>|<)\\s*(-?\\d+(\\.\\d+)?)"); + Matcher matcher = pattern.matcher(expression); + String variableName = ""; + double min = Double.POSITIVE_INFINITY; + double max = Double.NEGATIVE_INFINITY; + + while (matcher.find()) { + if (variableName.isEmpty()) { + variableName = matcher.group(1); + } + + String operator = matcher.group(2); + double value = Double.parseDouble(matcher.group(3)); + // Round the value to 5 decimal places + BigDecimal roundedValue = new BigDecimal(value).setScale(5, RoundingMode.HALF_UP); + value = roundedValue.doubleValue(); + + switch (operator) { + case ">": + min = Math.min(min, value + 0.00001); + break; + case "<": + max = Math.max(max, value - 0.00001); + break; + case ">=": + min = Math.min(min, value); + break; + case "<=": + max = Math.max(max, value); + break; + } + } + + // Adjust min and max values for the entire expression + if (min == Double.POSITIVE_INFINITY) min = Double.NEGATIVE_INFINITY; + if (max == Double.NEGATIVE_INFINITY) max = Double.POSITIVE_INFINITY; + + // Round the final min and max values to 5 decimal places + BigDecimal roundedMin = new BigDecimal(min).setScale(5, RoundingMode.HALF_UP); + BigDecimal roundedMax = new BigDecimal(max).setScale(5, RoundingMode.HALF_UP); + + return new VariableBoundsDTO(variableName, roundedMin.doubleValue(), roundedMax.doubleValue()); + } + public static void main(String[] args) { + String expression = "($['AT000001_delta_accrual']<100)"; + // VariableBoundsDTO VariableExprBoundsDTO= extractMinMax(expression); + + VariableBoundsDTO variableExprBoundsDTO=extractMinMax(expression); + System.out.println(variableExprBoundsDTO); + String targetId = "{\"['bffffff2_temperature']\":12,\"['bffffff2_humidity']\":34}"; + Map keyDataMap = JSON.parseObject(targetId, new TypeReference<>() { + }); + + String rawData="{\"bffffff2_temperature\": 0}"; + Map rawDataMap = JSON.parseObject(rawData, new TypeReference<>() { + }); + + System.out.println(rawDataMap.keySet()); + + keyDataMap.keySet().stream().forEach((key)->{ + String formattedKey = key.replaceAll("[\\[\\]'\" ]", ""); + if (rawDataMap.containsKey(formattedKey)){ + System.out.println("Key found: " + formattedKey); + } + }); + + } + + + // 更新的正则表达式,能够处理更复杂的嵌套模式 +// private static final Pattern JSON_PATH_PATTERN = Pattern.compile("\\$\\.(?:[a-zA-Z_0-9]+(?:\\[\\d+\\])*(?:\\.[a-zA-Z_0-9]+(?:\\[\\d+\\])*)*)" +// ); + private static final Pattern JSON_PATH_PATTERN = Pattern.compile( + "\\$\\.(?:[a-zA-Z_0-9]+(?:\\[\\d+\\]|\\['[a-zA-Z_0-9\\-_]+'\\])*(?:\\.[a-zA-Z_0-9]+(?:\\[\\d+\\]|\\['[a-zA-Z_0-9\\-_]+'\\])*)*)|\\$\\['[a-zA-Z_0-9\\-_]+'\\]" + ); + public static Set extractVariables(String expression) { + if (StringUtils.isEmpty(expression)){ + return new HashSet<>(); + } + Matcher matcher = JSON_PATH_PATTERN.matcher(expression); + Set variables = new HashSet<>(); + + while (matcher.find()) { + variables.add(matcher.group()); + } + + return variables; + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/utils/DateUtils.java b/src/main/java/com/techsor/datacenter/sender/utils/DateUtils.java new file mode 100644 index 0000000..1c13d8d --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/utils/DateUtils.java @@ -0,0 +1,150 @@ +package com.techsor.datacenter.sender.utils; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.fastjson2.JSON; +import com.techsor.datacenter.sender.entitiy.ComplexTime; + +public class DateUtils { + + public static void main(String[] args) { + + System.out.println(JSON.toJSONString(getComplexTime(1760141647951L))); + + } + + public static ComplexTime getComplexTime(long timestamp) { + if (String.valueOf(timestamp).length() == 10) { + timestamp *= 1000; + } + + Instant instant = Instant.ofEpochMilli(timestamp); + ZoneId tokyoZone = ZoneId.of("Asia/Tokyo"); + ZonedDateTime tokyoTime = instant.atZone(tokyoZone); + + // 获取上一天 + ZonedDateTime previousDay = tokyoTime.minusDays(1); + + // 格式化 + DateTimeFormatter dateFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String currentDate = tokyoTime.format(dateFmt); + String currentDateH = tokyoTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH")); + String currentDateHM = tokyoTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm")); + String previousDate = previousDay.format(dateFmt); + + // 构造对象 + ComplexTime complexTime = new ComplexTime(); + complexTime.setDateKey(currentDate); + complexTime.setDateHKey(currentDateH); + complexTime.setDateHMKey(currentDateHM); + complexTime.setPreviousDateKey(previousDate); + complexTime.setYearKey(tokyoTime.getYear()); + complexTime.setMonthKey(tokyoTime.getMonthValue()); + complexTime.setDayKey(tokyoTime.getDayOfMonth()); + complexTime.setHourKey(tokyoTime.getHour()); + complexTime.setMinuteKey(tokyoTime.getMinute()); + complexTime.setSecondKey(tokyoTime.getSecond()); + + return complexTime; + } + + + + + public static String getCurrentDate(){ + ZoneId zoneId = ZoneId.of("Asia/Tokyo"); + LocalDateTime now = LocalDateTime.now(zoneId); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String formatDateTime = now.format(formatter); + return formatDateTime; + } + + public static Map getComplexDate() { + ZoneId zoneId = ZoneId.of("Asia/Tokyo"); + LocalDateTime now = LocalDateTime.now(zoneId); + + DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("yyyy"); + DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("MM"); + DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("dd"); + + String currentDate = now.format(dateFormatter); + String currentYear = now.format(yearFormatter); + String currentMonth = now.format(monthFormatter); + String currentDay = now.format(dayFormatter); + + Map dateDetails = new HashMap<>(); + dateDetails.put("dateKey", currentDate); + dateDetails.put("yearKey", currentYear); + dateDetails.put("monthKey", currentMonth); + dateDetails.put("dayKey", currentDay); + + return dateDetails; + } + + + // 定义常见的日期时间格式 + private static final List DATE_FORMATTERS = Arrays.asList( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyy/MM/dd'T'HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyy-MM-dd-HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyy/MM/dd-HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyyMMdd'T'HH:mm:ss"), + DateTimeFormatter.ISO_OFFSET_DATE_TIME + ); + + /** + * pareStringDateTime to Ts + * @param dateString + * @return + */ + public static long parseToTimestamp(String dateString) { + for (DateTimeFormatter formatter : DATE_FORMATTERS) { + try { + // 解析时间字符串为LocalDateTime对象 + LocalDateTime localDateTime = LocalDateTime.parse(dateString, formatter); + // 将LocalDateTime转换为ZonedDateTime,使用日本时区 + ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Tokyo")); + // 获取时间戳(以毫秒为单位) + return zonedDateTime.toInstant().toEpochMilli(); + } catch (DateTimeParseException e) { + // 继续尝试下一个格式 + } + } + throw new IllegalArgumentException("Invalid date format: " + dateString); + } + + //根据Format转换毫秒时间戳 + public static String convertTimestampToFormattedDateTime(String timestamp, String format) { + LocalDateTime dateTime = Instant.ofEpochMilli(Long.parseLong(timestamp)) + .atZone(ZoneId.of("Asia/Tokyo")) + .toLocalDateTime(); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format); + return dateTime.format(formatter); + } + + public static String convertMillisToHourMinuteFormat(long millis) { + if ( millis < 0) { + throw new IllegalArgumentException("Millis must be a non-negative number"); + } + long totalMinutes = millis / 60000; // 将毫秒转换为分钟 + long hours = totalMinutes / 60; // 计算小时数 + long minutes = totalMinutes % 60; // 计算剩余分钟数 + return String.format("%d:%02d", hours, minutes); + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/utils/HexUtils.java b/src/main/java/com/techsor/datacenter/sender/utils/HexUtils.java new file mode 100644 index 0000000..44d44a8 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/utils/HexUtils.java @@ -0,0 +1,41 @@ +package com.techsor.datacenter.sender.utils; + +public class HexUtils { + + /** + * 将二进制转换成16进制 + * + * @param buf + * @return + */ + public static String parseByte2HexStr(byte buf[]) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < buf.length; i++) { + String hex = Integer.toHexString(buf[i] & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + sb.append(hex.toUpperCase()); + } + return sb.toString(); + } + + /** + * 将16进制转换为二进制 + * + * @param hexStr + * @param length 补充0满足长度 + * @return + */ + public static String parseHexStr2Binary(String hexStr,Integer length) { + Integer octValue = Integer.valueOf((Integer.valueOf(hexStr,16))); + String binaryString = Integer.toBinaryString(octValue); + + while(binaryString.length() headerMap) { + logger.info("Get request, url:%s, header:%s\n", httpurl, JSONObject.toJSONString(headerMap)); + HttpURLConnection connection = null; + InputStream is = null; + BufferedReader br = null; + String result = null;// 返回结果字符串 + try { + // 创建远程url连接对象 + URL url = new URL(httpurl); + // 通过远程url连接对象打开一个连接,强转成httpURLConnection类 + connection = (HttpURLConnection) url.openConnection(); + //设置header + if (null != headerMap && !headerMap.isEmpty()) { + for (Map.Entry item : headerMap.entrySet()) { + connection.setRequestProperty(item.getKey().toString(),item.getValue().toString());//设置header + } + } + // 设置连接方式:get + connection.setRequestMethod("GET"); + // 设置连接主机服务器的超时时间:15000毫秒 + connection.setConnectTimeout(15000); + // 设置读取远程返回的数据时间:60000毫秒 + connection.setReadTimeout(60000); + // 发送请求 + connection.connect(); + // 通过connection连接,获取输入流 + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { + is = connection.getInputStream(); + } else { + is = connection.getErrorStream(); + } + // 封装输入流is,并指定字符集 + br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + // 存放数据 + StringBuffer sbf = new StringBuffer(); + String temp = null; + while ((temp = br.readLine()) != null) { + sbf.append(temp); + sbf.append("\r\n"); + } + result = sbf.toString(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 关闭资源 + if (null != br) { + try { + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (null != is) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + connection.disconnect();// 关闭远程连接 + } + + return result; + } + + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param httpUrl + * 发送请求的 URL + * @param param + * 请求参数应该是{"key":"==g43sEvsUcbcunFv3mHkIzlHO4iiUIT R7WwXuSVKTK0yugJnZSlr6qNbxsL8OqCUAFyCDCoRKQ882m6cTTi0q9uCJsq JJvxS+8mZVRP/7lWfEVt8/N9mKplUA68SWJEPSXyz4MDeFam766KEyvqZ99d"}的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String doPost(String httpUrl, String param, HashMap headerMap) { + logger.info("doPost请求, url:{}, param:{}, headerMap:{}", httpUrl, param, JSONObject.toJSONString(headerMap)); + + HttpURLConnection connection = null; + InputStream is = null; + OutputStream os = null; + BufferedReader br = null; + String result = null; + try { + URL url = new URL(httpUrl); + // 通过远程url连接对象打开连接 + connection = (HttpURLConnection) url.openConnection(); + //设置header + if (null != headerMap && !headerMap.isEmpty()) { + for (Map.Entry item : headerMap.entrySet()) { + connection.setRequestProperty(item.getKey().toString(),item.getValue().toString());//设置header + } + } + // 设置连接请求方式 + connection.setRequestMethod("POST"); + // 设置连接主机服务器超时时间:15000毫秒 + connection.setConnectTimeout(15000); + // 设置读取主机服务器返回数据超时时间:60000毫秒 + connection.setReadTimeout(60000); + + // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true + connection.setDoOutput(true); + // 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无 + connection.setDoInput(true); + // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。 + if (null == headerMap || (!headerMap.containsKey("Content-Type") && !headerMap.containsKey("content-type"))) { + connection.setRequestProperty("Content-Type", "application/json"); + } + // 设置鉴权信息:Authorization: Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0 + //connection.setRequestProperty("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0"); + // 通过连接对象获取一个输出流 + os = connection.getOutputStream(); + // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的 + os.write(param.getBytes()); + // 通过连接对象获取一个输入流,向远程读取 + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { + is = connection.getInputStream(); + } else { + is = connection.getErrorStream(); + } + // 对输入流对象进行包装:charset根据工作项目组的要求来设置 + br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + + StringBuffer sbf = new StringBuffer(); + String temp = null; + // 循环遍历一行一行读取数据 + while ((temp = br.readLine()) != null) { + sbf.append(temp); + sbf.append("\r\n"); + } + result = sbf.toString(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + // 关闭资源 + if (null != br) { + try { + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (null != os) { + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (null != is) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + // 断开与远程地址url的连接 + connection.disconnect(); + } + return result; + } + + + public static HttpResponse doPostWithRespone(String httpUrl, String param, HashMap headerMap) { + logger.info("doPostWithRespone请求, url:{}, param:{}, headerMap:{}", httpUrl, param, JSONObject.toJSONString(headerMap)); + + HttpURLConnection connection = null; + InputStream is = null; + OutputStream os = null; + BufferedReader br = null; + String result = null; + HttpResponse httpResponse = new HttpResponse(); + try { + URL url = new URL(httpUrl); + // 通过远程url连接对象打开连接 + connection = (HttpURLConnection) url.openConnection(); + //设置header + if (null != headerMap && !headerMap.isEmpty()) { + for (Map.Entry item : headerMap.entrySet()) { + connection.setRequestProperty(item.getKey().toString(),item.getValue().toString());//设置header + } + } + // 设置连接请求方式 + connection.setRequestMethod("POST"); + // 设置连接主机服务器超时时间:15000毫秒 + connection.setConnectTimeout(15000); + // 设置读取主机服务器返回数据超时时间:60000毫秒 + connection.setReadTimeout(60000); + + // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true + connection.setDoOutput(true); + // 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无 + connection.setDoInput(true); + // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。 + if (null == headerMap || (!headerMap.containsKey("Content-Type") && !headerMap.containsKey("content-type"))) { + connection.setRequestProperty("Content-Type", "application/json"); + } + // 设置鉴权信息:Authorization: Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0 + //connection.setRequestProperty("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0"); + // 通过连接对象获取一个输出流 + os = connection.getOutputStream(); + // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的 + os.write(param.getBytes()); + // 通过连接对象获取一个输入流,向远程读取 + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { + is = connection.getInputStream(); + } else { + is = connection.getErrorStream(); + } + // 对输入流对象进行包装:charset根据工作项目组的要求来设置 + br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + + StringBuffer sbf = new StringBuffer(); + String temp = null; + // 循环遍历一行一行读取数据 + while ((temp = br.readLine()) != null) { + sbf.append(temp); + sbf.append("\r\n"); + } + result = sbf.toString(); + + httpResponse.setCode(connection.getResponseCode()); + httpResponse.setMsg(result); + + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + // 关闭资源 + if (null != br) { + try { + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (null != os) { + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (null != is) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + // 断开与远程地址url的连接 + connection.disconnect(); + } + return httpResponse; + } + +} + diff --git a/src/main/java/com/techsor/datacenter/sender/utils/RedisUtils.java b/src/main/java/com/techsor/datacenter/sender/utils/RedisUtils.java new file mode 100644 index 0000000..0737795 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/utils/RedisUtils.java @@ -0,0 +1,215 @@ +package com.techsor.datacenter.sender.utils; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +public class RedisUtils { + @Resource + private RedisTemplate stringRedisTemplate; + + private final String DEFAULT_KEY_PREFIX = ""; + private final int EXPIRE_TIME = 1; + private final TimeUnit EXPIRE_TIME_TYPE = TimeUnit.DAYS; + + + /** + * 数据缓存至redis + * + * @param key + * @param value + * @return + */ + public void add(K key, String value) { + try { + if (value != null) { + stringRedisTemplate + .opsForValue() + .set(DEFAULT_KEY_PREFIX + key, value); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("数据缓存至redis失败"); + } + } + + /** + * 数据缓存至redis并设置过期时间 + * + * @param key + * @param value + * @return + */ + public void add(K key, String value, long timeout, TimeUnit unit) { + try { + if (value != null) { + stringRedisTemplate + .opsForValue() + .set(DEFAULT_KEY_PREFIX + key, value, timeout, unit); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("数据缓存至redis失败"); + } + } + + /** + * 写入 hash-set,已经是key-value的键值,不能再写入为hash-set + * + * @param key must not be {@literal null}. + * @param subKey must not be {@literal null}. + * @param value 写入的值 + */ + public void addHashCache(K key, SK subKey, V value) { + stringRedisTemplate.opsForHash().put(DEFAULT_KEY_PREFIX + key, subKey, value); + } + + /** + * 写入 hash-set,并设置过期时间 + * + * @param key must not be {@literal null}. + * @param subKey must not be {@literal null}. + * @param value 写入的值 + */ + public void addHashCache(K key, SK subKey, V value, long timeout, TimeUnit unit) { + stringRedisTemplate.opsForHash().put(DEFAULT_KEY_PREFIX + key, subKey, value); + stringRedisTemplate.expire(DEFAULT_KEY_PREFIX + key, timeout, unit); + } + + /** + * 获取 hash-setvalue + * + * @param key must not be {@literal null}. + * @param subKey must not be {@literal null}. + */ + public Object getHashCache(K key, SK subKey) { + return stringRedisTemplate.opsForHash().get(DEFAULT_KEY_PREFIX + key, subKey); + } + + + /** + * 从redis中获取缓存数据,转成对象 + * + * @param key must not be {@literal null}. + * @param clazz 对象类型 + * @return + */ + public V getObject(K key, Class clazz) { + String value = this.get(key); + V result = null; + if (!StringUtils.isEmpty(value)) { + result = JSONObject.parseObject(value, clazz); + } + return result; + } + + /** + * 从redis中获取缓存数据,转成list + * + * @param key must not be {@literal null}. + * @param clazz 对象类型 + * @return + */ + public List getList(K key, Class clazz) { + String value = this.get(key); + List result = Collections.emptyList(); + if (!StringUtils.isEmpty(value)) { + result = JSONArray.parseArray(value, clazz); + } + return result; + } + + /** + * 功能描述:Get the value of {@code key}. + * + * @param key must not be {@literal null}. + * @return java.lang.String + * @date 2021/9/19 + **/ + public String get(K key) { + String value; + try { + value = (String) stringRedisTemplate.opsForValue().get(DEFAULT_KEY_PREFIX + key); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("从redis缓存中获取缓存数据失败"); + } + return value; + } + + public Integer getInteger(K key,Integer defaultValue) { + String value; + Integer intValue; + try { + value = (String) stringRedisTemplate.opsForValue().get(DEFAULT_KEY_PREFIX + key); + if (value==null){ + return defaultValue; + }else{ + return Integer.parseInt(value); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("从redis缓存中获取缓存数据失败"); + } + } + + public String getString(K key,String defaultValue) { + String value; + try { + value = (String) stringRedisTemplate.opsForValue().get(key); + if (value==null){ + return defaultValue; + }else{ + return value; + } + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException("从redis缓存中获取缓存数据失败"); + } + } + + /** + * 删除key + */ + public void delete(String key) { + stringRedisTemplate.delete(key); + } + + /** + * 批量删除key + */ + public void delete(Collection keys) { + stringRedisTemplate.delete(keys); + } + + /** + * 序列化key + */ + public byte[] dump(String key) { + return stringRedisTemplate.dump(key); + } + + /** + * 是否存在key + */ + public Boolean hasKey(String key) { + return stringRedisTemplate.hasKey(key); + } + +} + diff --git a/src/main/java/com/techsor/datacenter/sender/utils/Sha1Utils.java b/src/main/java/com/techsor/datacenter/sender/utils/Sha1Utils.java new file mode 100644 index 0000000..fd4d96e --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/utils/Sha1Utils.java @@ -0,0 +1,94 @@ +package com.techsor.datacenter.sender.utils; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +public class Sha1Utils { + private static final String MAC_NAME = "HmacSHA1"; + private static final String ENCODING = "UTF-8"; + private static final String HMAC_SHA1 = "HmacSHA1"; + + /** + * + * @param pwd + * @param data + * @return + * @throws NoSuchAlgorithmException + * @throws UnsupportedEncodingException + * @throws InvalidKeyException + */ + public static byte[] encode(String pwd, String data) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException { + Mac mac = Mac.getInstance("HmacSHA1"); + byte[] keyBytes = pwd.getBytes("UTF8"); + SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1"); + mac.init(signingKey); + byte[] utf8 = data.getBytes("UTF8"); + return mac.doFinal(utf8); + } + + /** + * + * @param Passwd + * @return + * @throws NoSuchAlgorithmException + */ + public static String encode(String Passwd) throws NoSuchAlgorithmException { + MessageDigest alg = MessageDigest.getInstance("SHA-1"); + alg.update(Passwd.getBytes()); + byte[] bts = alg.digest(); + String result = ""; + String tmp = ""; + for (byte bt : bts) { + tmp = (Integer.toHexString(bt & 0xFF)); + if (tmp.length() == 1) { + result += "0"; + } + result += tmp; + } + return result; + } + + /** + * 生成签名数据_HmacSHA1加密 + * + * @param data + * 待加密的数据 + * @param key + * 加密使用的key + * @throws InvalidKeyException + * @throws NoSuchAlgorithmException + */ + public static String getSignature(String encryptText, String encryptKey) throws Exception { + + byte[] key=encryptKey.getBytes(); + //byte[] key="myappsecret".getBytes(); + SecretKeySpec signingKey = new SecretKeySpec(key, HMAC_SHA1); + Mac mac = Mac.getInstance(HMAC_SHA1); + mac.init(signingKey); + byte[] rawHmac = mac.doFinal(encryptText.getBytes()); + StringBuilder sb=new StringBuilder(); + sb.append( Base64.getEncoder().encodeToString(rawHmac)); + return sb.toString(); + } + + private static String byte2hex(final byte[] b) { + String hs = ""; + String stmp = ""; + for (int n = 0; n < b.length; n++) { + // 以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式。 + stmp = (Integer.toHexString(b[n] & 0xFF)); + if (stmp.length() == 1) { + hs = hs + "0" + stmp; + } else { + hs = hs + stmp; + } + } + return hs; + } + +} diff --git a/src/main/java/com/techsor/datacenter/sender/utils/SpringUtils.java b/src/main/java/com/techsor/datacenter/sender/utils/SpringUtils.java new file mode 100644 index 0000000..eae8071 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/utils/SpringUtils.java @@ -0,0 +1,29 @@ +package com.techsor.datacenter.sender.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +@Component +public class SpringUtils implements ApplicationContextAware { + + private static ApplicationContext applicationContext = null; + + + + private SpringUtils(){ + + } + + public static T getBean(String beanName,Class clazz){ + + return (T) applicationContext.getBean(beanName,clazz); + } + + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringUtils.applicationContext =applicationContext; + } +} diff --git a/src/main/java/com/techsor/datacenter/sender/utils/TimeUtils.java b/src/main/java/com/techsor/datacenter/sender/utils/TimeUtils.java new file mode 100644 index 0000000..ac31a49 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/utils/TimeUtils.java @@ -0,0 +1,57 @@ +package com.techsor.datacenter.sender.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.TimeZone; + +public class TimeUtils { + + + public static long strToUTCUnixTime(String str) throws ParseException { + SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df2.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = df2.parse(str.toString()); + return date.getTime(); + } + + + + + public static String getCurrentDate(){ + ZoneId zoneId = ZoneId.of("Asia/Tokyo"); + LocalDateTime now = LocalDateTime.now(zoneId); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String formatDateTime = now.format(formatter); + return formatDateTime; + } + + public static String kingiOServerTimeToTs(String time){ + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + LocalDateTime localDateTime = LocalDateTime.parse(time, formatter); + ZoneId japanZone = ZoneId.of("Asia/Tokyo"); + ZonedDateTime zonedDateTime = localDateTime.atZone(japanZone); + long epochMilli = zonedDateTime.toInstant().toEpochMilli(); + + // 打印结果 +// System.out.println("毫秒时间戳: " + epochMilli); + return String.valueOf(epochMilli); + } + + public static String st150TimeToTs(String timeStr){ + // 定义时间格式 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"); + // 解析时间字符串 + OffsetDateTime offsetDateTime = OffsetDateTime.parse(timeStr, formatter); + // 转换为毫秒时间戳 + long timestamp = offsetDateTime.toInstant().toEpochMilli(); + return String.valueOf(timestamp); + } + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/vo/TargetForwardConfigVO.java b/src/main/java/com/techsor/datacenter/sender/vo/TargetForwardConfigVO.java new file mode 100644 index 0000000..dd24db8 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/vo/TargetForwardConfigVO.java @@ -0,0 +1,24 @@ +package com.techsor.datacenter.sender.vo; + +import lombok.Data; + +@Data +public class TargetForwardConfigVO { + + + private Long id; + + + //转发目标编码 + private String targetForwardCode; + + + private String name; + + + //转发目标类型,MQTT或者HTTP + private String targetForwardType; + + + private String targetForwardParams; +} diff --git a/src/main/java/net/objecthunter/exp4j/ArrayStack.java b/src/main/java/net/objecthunter/exp4j/ArrayStack.java new file mode 100644 index 0000000..441b429 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/ArrayStack.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 Federico Vera + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.objecthunter.exp4j; + +import java.util.EmptyStackException; + +/** + * Simple double stack using a double array as data storage + * + * @author Federico Vera (dktcoding [at] gmail) + */ +class ArrayStack { + + private double[] data; + + private int idx; + + ArrayStack() { + this(5); + } + + ArrayStack(int initialCapacity) { + if (initialCapacity <= 0) { + throw new IllegalArgumentException( + "Stack's capacity must be positive"); + } + + data = new double[initialCapacity]; + idx = -1; + } + + void push(double value) { + if (idx + 1 == data.length) { + double[] temp = new double[(int) (data.length * 1.2) + 1]; + System.arraycopy(data, 0, temp, 0, data.length); + data = temp; + } + + data[++idx] = value; + } + + double peek() { + if (idx == -1) { + throw new EmptyStackException(); + } + return data[idx]; + } + + double pop() { + if (idx == -1) { + throw new EmptyStackException(); + } + return data[idx--]; + } + + boolean isEmpty() { + return idx == -1; + } + + int size() { + return idx + 1; + } +} diff --git a/src/main/java/net/objecthunter/exp4j/ArrayValueStack.java b/src/main/java/net/objecthunter/exp4j/ArrayValueStack.java new file mode 100644 index 0000000..a519700 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/ArrayValueStack.java @@ -0,0 +1,55 @@ +package net.objecthunter.exp4j; + +import java.util.EmptyStackException; + +public class ArrayValueStack { + private Object[] data; + + private int idx; + + ArrayValueStack() { + this(5); + } + + ArrayValueStack(int initialCapacity) { + if (initialCapacity <= 0) { + throw new IllegalArgumentException( + "Stack's capacity must be positive"); + } + + data = new Object[initialCapacity]; + idx = -1; + } + + void push(Object value) { + if (idx + 1 == data.length) { + Object[] temp = new Object[(int) (data.length * 1.2) + 1]; + System.arraycopy(data, 0, temp, 0, data.length); + data = temp; + } + + data[++idx] = value; + } + + Object peek() { + if (idx == -1) { + throw new EmptyStackException(); + } + return data[idx]; + } + + Object pop() { + if (idx == -1) { + throw new EmptyStackException(); + } + return data[idx--]; + } + + boolean isEmpty() { + return idx == -1; + } + + int size() { + return idx + 1; + } +} diff --git a/src/main/java/net/objecthunter/exp4j/Expression.java b/src/main/java/net/objecthunter/exp4j/Expression.java new file mode 100644 index 0000000..2f21b70 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/Expression.java @@ -0,0 +1,263 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j; + +import net.objecthunter.exp4j.function.Function; +import net.objecthunter.exp4j.function.Functions; +import net.objecthunter.exp4j.operator.Operator; + + +import net.objecthunter.exp4j.tokenizer.*; + +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +public class Expression { + + private final Token[] tokens; + + private final Map variables; + + private final Set userFunctionNames; + + private static Map createDefaultVariables() { + final Map vars = new HashMap<>(4); + vars.put("pi", Math.PI); + vars.put("π", Math.PI); + vars.put("φ", 1.61803398874d); + vars.put("e", Math.E); + return vars; + } + + /** + * Creates a new expression that is a copy of the existing one. + * + * @param existing the expression to copy + */ + public Expression(final Expression existing) { + this.tokens = Arrays.copyOf(existing.tokens, existing.tokens.length); + this.variables = new HashMap<>(); + this.variables.putAll(existing.variables); + this.userFunctionNames = new HashSet<>(existing.userFunctionNames); + } + + Expression(final Token[] tokens) { + this.tokens = tokens; + this.variables = createDefaultVariables(); + this.userFunctionNames = Collections.emptySet(); + } + + Expression(final Token[] tokens, Set userFunctionNames) { + this.tokens = tokens; + this.variables = createDefaultVariables(); + this.userFunctionNames = userFunctionNames; + } + + public Expression setVariable(final String name, final double value) { + this.checkVariableName(name); + this.variables.put(name, value); + return this; + } + + private void checkVariableName(String name) { + if (this.userFunctionNames.contains(name) || Functions.getBuiltinFunction(name) != null) { + throw new IllegalArgumentException("The variable name '" + name + "' is invalid. Since there exists a function with the same name"); + } + } + + public Expression setVariables(Map variables) { + for (Map.Entry v : variables.entrySet()) { + this.setVariable(v.getKey(), v.getValue()); + } + return this; + } + + public Expression clearVariables() { + this.variables.clear(); + return this; + } + + public Set getVariableNames() { + final Set variables = new HashSet<>(); + for (final Token t : tokens) { + if (t.getType() == Token.TOKEN_VARIABLE) + variables.add(((VariableToken) t).getName()); + } + return variables; + } + + public ValidationResult validate(boolean checkVariablesSet) { + final List errors = new ArrayList<>(0); + if (checkVariablesSet) { + /* check that all vars have a value set */ + for (final Token t : this.tokens) { + if (t.getType() == Token.TOKEN_VARIABLE) { + final String var = ((VariableToken) t).getName(); + if (!variables.containsKey(var)) { + errors.add("The setVariable '" + var + "' has not been set"); + } + } + } + } + + /* Check if the number of operands, functions and operators match. + The idea is to increment a counter for operands and decrease it for operators. + When a function occurs the number of available arguments has to be greater + than or equals to the function's expected number of arguments. + The count has to be larger than 1 at all times and exactly 1 after all tokens + have been processed */ + int count = 0; + for (Token tok : this.tokens) { + switch (tok.getType()) { + case Token.TOKEN_NUMBER: + case Token.TOKEN_VARIABLE: + count++; + break; + case Token.TOKEN_FUNCTION: + final Function func = ((FunctionToken) tok).getFunction(); + final int argsNum = func.getNumArguments(); + if (argsNum > count) { + errors.add("Not enough arguments for '" + func.getName() + "'"); + } + if (argsNum > 1) { + count -= argsNum - 1; + } else if (argsNum == 0) { + // see https://github.com/fasseg/exp4j/issues/59 + count++; + } + break; + case Token.TOKEN_OPERATOR: + Operator op = ((OperatorToken) tok).getOperator(); + if (op.getNumOperands() == 2) { + count--; + } + break; + } + if (count < 1) { + errors.add("Too many operators"); + return new ValidationResult(false, errors); + } + } + if (count > 1) { + errors.add("Too many operands"); + } + return errors.size() == 0 ? ValidationResult.SUCCESS : new ValidationResult(false, errors); + + } + + public ValidationResult validate() { + return validate(true); + } + + public Future evaluateAsync(ExecutorService executor) { + return executor.submit(this::evaluateValue); + } + +// public double evaluate() { +// final ArrayStack output = new ArrayStack(); +// for (Token t : tokens) { +// if (t.getType() == Token.TOKEN_NUMBER) { +// output.push(((NumberToken) t).getValue()); +// } else if (t.getType() == Token.TOKEN_VARIABLE) { +// final String name = ((VariableToken) t).getName(); +// final Double value = this.variables.get(name); +// if (value == null) { +// throw new IllegalArgumentException("No value has been set for the setVariable '" + name + "'."); +// } +// output.push(value); +// } else if (t.getType() == Token.TOKEN_OPERATOR) { +// OperatorToken op = (OperatorToken) t; +// if (output.size() < op.getOperator().getNumOperands()) { +// throw new IllegalArgumentException("Invalid number of operands available for '" + op.getOperator().getSymbol() + "' operator"); +// } +// if (op.getOperator().getNumOperands() == 2) { +// /* pop the operands and push the result of the operation */ +// double rightArg = output.pop(); +// double leftArg = output.pop(); +// output.push(op.getOperator().apply(leftArg, rightArg)); +// } else if (op.getOperator().getNumOperands() == 1) { +// /* pop the operand and push the result of the operation */ +// double arg = output.pop(); +// output.push(op.getOperator().apply(arg)); +// } +// } else if (t.getType() == Token.TOKEN_FUNCTION) { +// FunctionToken func = (FunctionToken) t; +// final int numArguments = func.getFunction().getNumArguments(); +// if (output.size() < numArguments) { +// throw new IllegalArgumentException("Invalid number of arguments available for '" + func.getFunction().getName() + "' function"); +// } +// /* collect the arguments from the stack */ +// double[] args = new double[numArguments]; +// for (int j = numArguments - 1; j >= 0; j--) { +// args[j] = output.pop(); +// } +// output.push(func.getFunction().apply(args)); +// } +// } +// if (output.size() > 1) { +// throw new IllegalArgumentException("Invalid number of items on the output queue. Might be caused by an invalid number of arguments for a function."); +// } +// return output.pop(); +// } + + + public Object evaluateValue() { + final ArrayValueStack output = new ArrayValueStack(); + for (Token t : tokens) { + if (t.getType() == Token.TOKEN_NUMBER) { + output.push(((NumberToken) t).getValue()); + } else if (t.getType() == Token.TOKEN_VARIABLE) { + final String name = ((VariableToken) t).getName(); + final Object value = this.variables.get(name); + if (value == null) { + throw new IllegalArgumentException("No value has been set for the setVariable '" + name + "'."); + } + output.push(value); + } else if (t.getType() == Token.TOKEN_OPERATOR) { + OperatorToken op = (OperatorToken) t; + if (output.size() < op.getOperator().getNumOperands()) { + throw new IllegalArgumentException("Invalid number of operands available for '" + op.getOperator().getSymbol() + "' operator"); + } + if (op.getOperator().getNumOperands() == 2) { + /* pop the operands and push the result of the operation */ + Object rightArg = output.pop(); + Object leftArg = output.pop(); + output.push(op.getOperator().apply(leftArg, rightArg)); + } else if (op.getOperator().getNumOperands() == 1) { + /* pop the operand and push the result of the operation */ + output.push(op.getOperator().apply(output.pop())); + } + } else if (t.getType() == Token.TOKEN_FUNCTION) { + FunctionToken func = (FunctionToken) t; + final int numArguments = func.getFunction().getNumArguments(); + if (output.size() < numArguments) { + throw new IllegalArgumentException("Invalid number of arguments available for '" + func.getFunction().getName() + "' function"); + } + /* collect the arguments from the stack */ + Object[] args = new Object[numArguments]; + for (int j = numArguments - 1; j >= 0; j--) { + args[j] = output.pop(); + } + output.push(func.getFunction().apply(args)); + } + } + if (output.size() > 1) { + throw new IllegalArgumentException("Invalid number of items on the output queue. Might be caused by an invalid number of arguments for a function."); + } + return output.pop(); + } +} diff --git a/src/main/java/net/objecthunter/exp4j/ExpressionBuilder.java b/src/main/java/net/objecthunter/exp4j/ExpressionBuilder.java new file mode 100644 index 0000000..e006c4f --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/ExpressionBuilder.java @@ -0,0 +1,206 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.objecthunter.exp4j; + +import net.objecthunter.exp4j.function.Functions; +import net.objecthunter.exp4j.operator.Operator; +import net.objecthunter.exp4j.shuntingyard.ShuntingYard; +import net.objecthunter.exp4j.function.Function; + +import java.util.*; + +/** + * Factory class for {@link Expression} instances. This class is the main API entrypoint. Users should create new + * {@link Expression} instances using this factory class. + */ +public class ExpressionBuilder { + + private final String expression; + + private final Map userFunctions; + + private final Map userOperators; + + private final Set variableNames; + + private boolean implicitMultiplication = true; + + /** + * Create a new ExpressionBuilder instance and initialize it with a given expression string. + * + * @param expression the expression to be parsed + */ + public ExpressionBuilder(String expression) { + if (expression == null || expression.trim().length() == 0) { + throw new IllegalArgumentException("Expression can not be empty"); + } + this.expression = expression; + this.userOperators = new HashMap<>(4); + this.userFunctions = new HashMap<>(4); + this.variableNames = new HashSet<>(4); + } + + /** + * Add a {@link Function} implementation available for use in the expression + * + * @param function the custom {@link Function} implementation that should be available for use in the expression. + * @return the ExpressionBuilder instance + */ + public ExpressionBuilder function(Function function) { + this.userFunctions.put(function.getName(), function); + return this; + } + + /** + * Add multiple {@link Function} implementations available for use in the expression + * + * @param functions the custom {@link Function} implementations + * @return the ExpressionBuilder instance + */ + public ExpressionBuilder functions(Function... functions) { + for (Function f : functions) { + this.userFunctions.put(f.getName(), f); + } + return this; + } + + /** + * Add multiple {@link Function} implementations available for use in the expression + * + * @param functions A {@link List} of custom {@link Function} implementations + * @return the ExpressionBuilder instance + */ + public ExpressionBuilder functions(List functions) { + for (Function f : functions) { + this.userFunctions.put(f.getName(), f); + } + return this; + } + + /** + * Declare variable names used in the expression + * + * @param variableNames the variables used in the expression + * @return the ExpressionBuilder instance + */ + public ExpressionBuilder variables(Set variableNames) { + this.variableNames.addAll(variableNames); + return this; + } + + /** + * Declare variable names used in the expression + * + * @param variableNames the variables used in the expression + * @return the ExpressionBuilder instance + */ + public ExpressionBuilder variables(String... variableNames) { + Collections.addAll(this.variableNames, variableNames); + return this; + } + + /** + * Declare a variable used in the expression + * + * @param variableName the variable used in the expression + * @return the ExpressionBuilder instance + */ + public ExpressionBuilder variable(String variableName) { + this.variableNames.add(variableName); + return this; + } + + public ExpressionBuilder implicitMultiplication(boolean enabled) { + this.implicitMultiplication = enabled; + return this; + } + + /** + * Add an {@link Operator} which should be available for use in the expression + * + * @param operator the custom {@link Operator} to add + * @return the ExpressionBuilder instance + */ + public ExpressionBuilder operator(Operator operator) { + this.checkOperatorSymbol(operator); + this.userOperators.put(operator.getSymbol(), operator); + return this; + } + + private void checkOperatorSymbol(Operator op) { + String name = op.getSymbol(); + for (char ch : name.toCharArray()) { + if (!Operator.isAllowedOperatorChar(ch)) { + throw new IllegalArgumentException("The operator symbol '" + name + "' is invalid"); + } + } + } + + /** + * Add multiple {@link Operator} implementations which should be available for use in the expression + * + * @param operators the set of custom {@link Operator} implementations to add + * @return the ExpressionBuilder instance + */ + public ExpressionBuilder operator(Operator... operators) { + for (Operator o : operators) { + this.operator(o); + } + return this; + } + + /** + * Add multiple {@link Operator} implementations which should be available for use in the expression + * + * @param operators the {@link List} of custom {@link Operator} implementations to add + * @return the ExpressionBuilder instance + */ + public ExpressionBuilder operator(List operators) { + for (Operator o : operators) { + this.operator(o); + } + return this; + } + + /** + * Build the {@link Expression} instance using the custom operators and functions set. + * + * @return an {@link Expression} instance which can be used to evaluate the result of the expression + */ + public Expression build() { + if (expression.length() == 0) { + throw new IllegalArgumentException("The expression can not be empty"); + } + + /* set the constants' varibale names */ + variableNames.add("pi"); + variableNames.add("π"); + variableNames.add("e"); + variableNames.add("φ"); + + /* Check if there are duplicate vars/functions */ + for (String var : variableNames) { + if (Functions.getBuiltinFunction(var) != null || userFunctions.containsKey(var)) { + throw new IllegalArgumentException("A variable can not have the same name as a function [" + var + "]"); + } + } + + return new Expression(ShuntingYard.convertToRPN(this.expression, this.userFunctions, this.userOperators, + this.variableNames, this.implicitMultiplication), this.userFunctions.keySet()); + } + +} diff --git a/src/main/java/net/objecthunter/exp4j/ValidationResult.java b/src/main/java/net/objecthunter/exp4j/ValidationResult.java new file mode 100644 index 0000000..e7da025 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/ValidationResult.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j; + +import java.util.List; + +/** + * Contains the validation result for a given {@link Expression} + */ +public class ValidationResult { + private final boolean valid; + private final List errors; + + /** + * Create a new instance + * + * @param valid Whether the validation of the expression was successful + * @param errors The list of errors returned if the validation was unsuccessful + */ + public ValidationResult(boolean valid, List errors) { + this.valid = valid; + this.errors = errors; + } + + /** + * Check if an expression has been validated successfully + * + * @return true if the validation was successful, false otherwise + */ + public boolean isValid() { + return valid; + } + + /** + * Get the list of errors describing the issues while validating the expression + * + * @return The List of errors + */ + public List getErrors() { + return errors; + } + + /** + * A static class representing a successful validation result + */ + public static final ValidationResult SUCCESS = new ValidationResult(true, null); +} diff --git a/src/main/java/net/objecthunter/exp4j/function/Function.java b/src/main/java/net/objecthunter/exp4j/function/Function.java new file mode 100644 index 0000000..2732551 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/function/Function.java @@ -0,0 +1,124 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.objecthunter.exp4j.function; + +/** + * A class representing a Function which can be used in an expression + */ +public abstract class Function { + + private final String name; + + protected final int numArguments; + + /** + * Create a new Function with a given name and number of arguments + * + * @param name the name of the Function + * @param numArguments the number of arguments the function takes + */ + public Function(String name, int numArguments) { + if (numArguments < 0) { + throw new IllegalArgumentException("The number of function arguments can not be less than 0 for '" + + name + "'"); + } + if (!isValidFunctionName(name)) { + throw new IllegalArgumentException("The function name '" + name + "' is invalid"); + } + this.name = name; + this.numArguments = numArguments; + + } + + /** + * Create a new Function with a given name that takes a single argument + * + * @param name the name of the Function + */ + public Function(String name) { + this(name, 1); + } + + /** + * Get the name of the Function + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Get the number of arguments for this function + * + * @return the number of arguments + */ + public int getNumArguments() { + return numArguments; + } + + /** + * Method that does the actual calculation of the function value given the arguments + * + * @param args the set of arguments used for calculating the function + * @return the result of the function evaluation + */ + public abstract Object apply(Object... args); + + /** + * Get the set of characters which are allowed for use in Function names. + * + * @return the set of characters allowed + * @deprecated since 0.4.5 All unicode letters are allowed to be used in function names since 0.4.3. This API + * Function can be safely ignored. Checks for function name validity can be done using Character.isLetter() et al. + */ + public static char[] getAllowedFunctionCharacters() { + char[] chars = new char[53]; + int count = 0; + for (int i = 65; i < 91; i++) { + chars[count++] = (char) i; + } + for (int i = 97; i < 123; i++) { + chars[count++] = (char) i; + } + chars[count] = '_'; + return chars; + } + + public static boolean isValidFunctionName(final String name) { + if (name == null) { + return false; + } + + final int size = name.length(); + + if (size == 0) { + return false; + } + + for (int i = 0; i < size; i++) { + final char c = name.charAt(i); + if (Character.isLetter(c) || c == '_') { + continue; + } else if (Character.isDigit(c) && i > 0) { + continue; + } + return false; + } + return true; + } +} diff --git a/src/main/java/net/objecthunter/exp4j/function/Functions.java b/src/main/java/net/objecthunter/exp4j/function/Functions.java new file mode 100644 index 0000000..ed12d5c --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/function/Functions.java @@ -0,0 +1,610 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.function; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; + +/** + * Class representing the builtin functions available for use in expressions + */ +public class Functions { + + private static final int INDEX_SIN = 0; + private static final int INDEX_COS = 1; + private static final int INDEX_TAN = 2; + private static final int INDEX_CSC = 3; + private static final int INDEX_SEC = 4; + private static final int INDEX_COT = 5; + private static final int INDEX_SINH = 6; + private static final int INDEX_COSH = 7; + private static final int INDEX_TANH = 8; + private static final int INDEX_CSCH = 9; + private static final int INDEX_SECH = 10; + private static final int INDEX_COTH = 11; + private static final int INDEX_ASIN = 12; + private static final int INDEX_ACOS = 13; + private static final int INDEX_ATAN = 14; + private static final int INDEX_SQRT = 15; + private static final int INDEX_CBRT = 16; + private static final int INDEX_ABS = 17; + private static final int INDEX_CEIL = 18; + private static final int INDEX_FLOOR = 19; + private static final int INDEX_POW = 20; + private static final int INDEX_EXP = 21; + private static final int INDEX_EXPM1 = 22; + private static final int INDEX_LOG10 = 23; + private static final int INDEX_LOG2 = 24; + private static final int INDEX_LOG = 25; + private static final int INDEX_LOG1P = 26; + private static final int INDEX_LOGB = 27; + private static final int INDEX_SGN = 28; + private static final int INDEX_TO_RADIAN = 29; + private static final int INDEX_TO_DEGREE = 30; + + + //转换为二进制 + private static final int INDEX_DECIMAL_TO_BINARY = 31; + + //转换为十六进制 + private static final int INDEX_DECIMAL_TO_HEX = 32; + + //转换为二进制转换为十进制 + private static final int INDEX_BINARY_TO_DECIMAL = 33; + + //转换为十六进制转换为十进制 + private static final int INDEX_HEX_TO_DECIMAL = 34; + + + //二进制转换为十六进制 + private static final int INDEX_BINARY_TO_HEX = 35; + + //十六进制转换为二进制 + private static final int INDEX_HEX_TO_BINARY = 36; + + + //字符串是否以指定字符串开头 + private static final int INDEX_STRING_STARTSWITH= 37; + + + //字符串是否以指定字符串结尾 + private static final int INDEX_STRING_ENDSWITH= 38; + + //字符串是截取 + private static final int INDEX_STRING_SUBSTRING= 39; + + //字符串包含 + private static final int INDEX_STRING_CONTAINS= 40; + + + //bool 判断 + private static final int INDEX_BOOLEAN_IF= 41; + + + + private static final Function[] BUILT_IN_FUNCTIONS = new Function[37]; + + static { + BUILT_IN_FUNCTIONS[INDEX_SIN] = new Function("sin") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.sin((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for sin(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_COS] = new Function("cos") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.cos((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for cos(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_TAN] = new Function("tan") { + + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.tan((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for tan(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_COT] = new Function("cot") { + + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + double tan = Math.tan((Double) args[0]); + if (tan == 0d) { + throw new ArithmeticException("Division by zero in cotangent!"); + } + return 1d / tan; + } + throw new IllegalArgumentException("Invalid argument type for cot(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_LOG] = new Function("log") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.log((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for log(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_LOG2] = new Function("log2") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.log((Double) args[0]) / Math.log(2d); + } + throw new IllegalArgumentException("Invalid argument type for log2(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_LOG10] = new Function("log10") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.log10((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for log10(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_LOG1P] = new Function("log1p") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.log1p((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for log1p(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_ABS] = new Function("abs") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.abs((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for abs(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_ACOS] = new Function("acos") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.acos((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for acos(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_ASIN] = new Function("asin") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.asin((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for asin(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_ATAN] = new Function("atan") { + + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.atan((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for atan(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_CBRT] = new Function("cbrt") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.cbrt((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for cbrt(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_FLOOR] = new Function("floor") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.floor((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for floor(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_SINH] = new Function("sinh") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.sinh((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for sinh(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_SQRT] = new Function("sqrt") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.sqrt((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for sqrt(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_TANH] = new Function("tanh") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.tanh((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for tanh(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_COSH] = new Function("cosh") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.cosh((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for cosh(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_CEIL] = new Function("ceil") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.ceil((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for ceil(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_POW] = new Function("pow", 2) { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString()) && NumberUtils.isNumber(args[1].toString())){ + return Math.pow((Double) args[0], (Double) args[1]); + } + throw new IllegalArgumentException("Invalid argument type for pow(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_EXP] = new Function("exp", 1) { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.exp((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for exp(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_EXPM1] = new Function("expm1", 1) { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.expm1((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for expm1(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_SGN] = new Function("signum", 1) { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.signum((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for signum(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_CSC] = new Function("csc") { + + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return 1d / Math.sin((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for csc(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_SEC] = new Function("sec") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + double cos = Math.cos((Double) args[0]); + if (cos == 0d) { + throw new ArithmeticException("Division by zero in secant!"); + } + return 1d / cos; + } + throw new IllegalArgumentException("Invalid argument type for sec(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_CSCH] = new Function("csch") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + //this would throw an ArithmeticException later as sinh(0) = 0 + if ((Double) args[0] == 0d) { + return 0; + } + + return 1d / Math.sinh((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for csch(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_SECH] = new Function("sech") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + double cosh = Math.cosh((Double) args[0]); + if (cosh == 0d) { + throw new ArithmeticException("Division by zero in sech!"); + } + return 1d / cosh; + } + throw new IllegalArgumentException("Invalid argument type for sech(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_COTH] = new Function("coth") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + double sinh = Math.sinh((Double) args[0]); + if (sinh == 0d) { + throw new ArithmeticException("Division by zero in coth!"); + } + return Math.cosh((Double) args[0]) / sinh; + } + throw new IllegalArgumentException("Invalid argument type for coth(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_LOGB] = new Function("logb", 2) { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString()) && NumberUtils.isNumber(args[1].toString())){ + return Math.log((Double) args[1]) / Math.log((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for logb(): " + args[0].getClass().getSimpleName()); + } + + }; + BUILT_IN_FUNCTIONS[INDEX_TO_RADIAN] = new Function("toradian") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.toRadians((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for toradian(): " + args[0].getClass().getSimpleName()); + } + }; + BUILT_IN_FUNCTIONS[INDEX_TO_DEGREE] = new Function("todegree") { + + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Math.toDegrees((Double) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for todegree(): " + args[0].getClass().getSimpleName()); + } + }; + //转换为二进制 + BUILT_IN_FUNCTIONS[INDEX_DECIMAL_TO_BINARY] = new Function("toBinary") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Integer.toBinaryString((Integer) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for toBinary(): " + args[0].getClass().getSimpleName()); + } + }; + //转换为十六进制 + BUILT_IN_FUNCTIONS[INDEX_DECIMAL_TO_HEX] = new Function("toHex") { + @Override + public Object apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())){ + return Integer.toHexString((Integer) args[0]); + } + throw new IllegalArgumentException("Invalid argument type for toHex(): " + args[0].getClass().getSimpleName()); + } + }; + //二进制转换为十进制 + BUILT_IN_FUNCTIONS[INDEX_BINARY_TO_DECIMAL] = new Function("binaryToHex") { + @Override + public Object apply(Object... args) { + return Integer.parseInt(args[0].toString(), 2); + } + }; + //十六进制转换为十进制 + BUILT_IN_FUNCTIONS[INDEX_HEX_TO_DECIMAL] = new Function("binaryToDecimal") { + @Override + public Object apply(Object... args) { + int decimal = Integer.parseInt(args[0].toString(), 2); + return decimal; + } + }; + + BUILT_IN_FUNCTIONS[INDEX_STRING_STARTSWITH]=new Function("stringStartsWith") { + @Override + public Object apply(Object... args) { + if (args[0] instanceof String && args[1] instanceof String) { + return ((String) args[0]).startsWith((String) args[1]); + } + throw new IllegalArgumentException("Invalid argument type for startsWith(): " + args[0].getClass().getSimpleName()); + } + }; + + BUILT_IN_FUNCTIONS[INDEX_STRING_ENDSWITH]=new Function("stringEndsWith") { + @Override + public Object apply(Object... args) { + if (args[0] instanceof String && args[1] instanceof String) { + return ((String) args[0]).endsWith((String) args[1]); + } + throw new IllegalArgumentException("Invalid argument type for endsWith(): " + args[0].getClass().getSimpleName()); + } + }; + + + BUILT_IN_FUNCTIONS[INDEX_STRING_CONTAINS]=new Function("stringContains") { + @Override + public Object apply(Object... args) { + if (args[0] instanceof String && args[1] instanceof String) { + return ((String) args[0]).contains((String) args[1]); + } + throw new IllegalArgumentException("Invalid argument type for contains(): " + args[0].getClass().getSimpleName()); + } + }; + + //字符串截取stringSubstring("",1) + BUILT_IN_FUNCTIONS[INDEX_STRING_SUBSTRING]=new Function("stringSubstring") { + @Override + public Object apply(Object... args) { + if (args[0] instanceof String && args[1] instanceof Integer) { + return StringUtils.substring((String) args[0], (Integer) args[1]); + } + throw new IllegalArgumentException("Invalid argument type for indexOf(): " + args[0].getClass().getSimpleName()); + } + }; + + //bool 表达式 if(1>2,1,2) + BUILT_IN_FUNCTIONS[INDEX_BOOLEAN_IF]=new Function("if") { + @Override + public Object apply(Object... args) { + if (args[0] instanceof Boolean ) { + return (Boolean) args[0] ? args[1] : args[2]; + } + return null; + } + }; + + + } + + /** + * Get the builtin function for a given name + * + * @param name te name of the function + * @return a Function instance + */ + public static Function getBuiltinFunction(final String name) { + + switch (name) { + case "sin": + return BUILT_IN_FUNCTIONS[INDEX_SIN]; + case "cos": + return BUILT_IN_FUNCTIONS[INDEX_COS]; + case "tan": + return BUILT_IN_FUNCTIONS[INDEX_TAN]; + case "cot": + return BUILT_IN_FUNCTIONS[INDEX_COT]; + case "asin": + return BUILT_IN_FUNCTIONS[INDEX_ASIN]; + case "acos": + return BUILT_IN_FUNCTIONS[INDEX_ACOS]; + case "atan": + return BUILT_IN_FUNCTIONS[INDEX_ATAN]; + case "sinh": + return BUILT_IN_FUNCTIONS[INDEX_SINH]; + case "cosh": + return BUILT_IN_FUNCTIONS[INDEX_COSH]; + case "tanh": + return BUILT_IN_FUNCTIONS[INDEX_TANH]; + case "abs": + return BUILT_IN_FUNCTIONS[INDEX_ABS]; + case "log": + return BUILT_IN_FUNCTIONS[INDEX_LOG]; + case "log10": + return BUILT_IN_FUNCTIONS[INDEX_LOG10]; + case "log2": + return BUILT_IN_FUNCTIONS[INDEX_LOG2]; + case "log1p": + return BUILT_IN_FUNCTIONS[INDEX_LOG1P]; + case "ceil": + return BUILT_IN_FUNCTIONS[INDEX_CEIL]; + case "floor": + return BUILT_IN_FUNCTIONS[INDEX_FLOOR]; + case "sqrt": + return BUILT_IN_FUNCTIONS[INDEX_SQRT]; + case "cbrt": + return BUILT_IN_FUNCTIONS[INDEX_CBRT]; + case "pow": + return BUILT_IN_FUNCTIONS[INDEX_POW]; + case "exp": + return BUILT_IN_FUNCTIONS[INDEX_EXP]; + case "expm1": + return BUILT_IN_FUNCTIONS[INDEX_EXPM1]; + case "signum": + return BUILT_IN_FUNCTIONS[INDEX_SGN]; + case "csc": + return BUILT_IN_FUNCTIONS[INDEX_CSC]; + case "sec": + return BUILT_IN_FUNCTIONS[INDEX_SEC]; + case "csch": + return BUILT_IN_FUNCTIONS[INDEX_CSCH]; + case "sech": + return BUILT_IN_FUNCTIONS[INDEX_SECH]; + case "coth": + return BUILT_IN_FUNCTIONS[INDEX_COTH]; + case "toradian": + return BUILT_IN_FUNCTIONS[INDEX_TO_RADIAN]; + case "todegree": + return BUILT_IN_FUNCTIONS[INDEX_TO_DEGREE]; + case "toBinary": + return BUILT_IN_FUNCTIONS[INDEX_DECIMAL_TO_BINARY]; + case "toHex": + return BUILT_IN_FUNCTIONS[INDEX_DECIMAL_TO_HEX]; + case "binaryToDecimal": + return BUILT_IN_FUNCTIONS[INDEX_BINARY_TO_DECIMAL]; + case "hexToDecimal": + return BUILT_IN_FUNCTIONS[INDEX_HEX_TO_DECIMAL]; + case "stringStartsWith": + return BUILT_IN_FUNCTIONS[INDEX_STRING_STARTSWITH]; + case "stringEndsWith": + return BUILT_IN_FUNCTIONS[INDEX_STRING_ENDSWITH]; + case "stringContains": + return BUILT_IN_FUNCTIONS[INDEX_STRING_CONTAINS]; + case "stringSubstring": + return BUILT_IN_FUNCTIONS[INDEX_STRING_SUBSTRING]; + case "if": + return BUILT_IN_FUNCTIONS[INDEX_BOOLEAN_IF]; + default: + return null; + } + } + +} diff --git a/src/main/java/net/objecthunter/exp4j/operator/Operator.java b/src/main/java/net/objecthunter/exp4j/operator/Operator.java new file mode 100644 index 0000000..d45975e --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/operator/Operator.java @@ -0,0 +1,141 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.operator; + +/** + * Class representing operators that can be used in an expression + */ +public abstract class Operator { + /** + * The precedence value for the addition operation + */ + public static final int PRECEDENCE_ADDITION = 500; + /** + * The precedence value for the subtraction operation + */ + public static final int PRECEDENCE_SUBTRACTION = PRECEDENCE_ADDITION; + /** + * The precedence value for the multiplication operation + */ + public static final int PRECEDENCE_MULTIPLICATION = 1000; + /** + * The precedence value for the division operation + */ + public static final int PRECEDENCE_DIVISION = PRECEDENCE_MULTIPLICATION; + /** + * The precedence value for the modulo operation + */ + public static final int PRECEDENCE_MODULO = PRECEDENCE_DIVISION; + /** + * The precedence value for the power operation + */ + public static final int PRECEDENCE_POWER = 10000; + /** + * The precedence value for the unary minus operation + */ + public static final int PRECEDENCE_UNARY_MINUS = 5000; + /** + * The precedence value for the unary plus operation + */ + public static final int PRECEDENCE_UNARY_PLUS = PRECEDENCE_UNARY_MINUS; + + /** + * The set of allowed operator chars + */ + public static final char[] ALLOWED_OPERATOR_CHARS = {'+', '-', '*', '/', '%', '^', '!', '#', '§', + '$', '&', ';', ':', '~', '<', '>', '|', '=', '÷', '√', '∛', '⌈', '⌊'}; + + private final int numOperands; + private final boolean leftAssociative; + private final String symbol; + private final int precedence; + + /** + * Create a new operator for use in expressions + * + * @param symbol the symbol of the operator + * @param numberOfOperands the number of operands the operator takes (1 or 2) + * @param leftAssociative set to true if the operator is left associative, false if it is right associative + * @param precedence the precedence value of the operator + */ + public Operator(String symbol, int numberOfOperands, boolean leftAssociative, + int precedence) { + super(); + this.numOperands = numberOfOperands; + this.leftAssociative = leftAssociative; + this.symbol = symbol; + this.precedence = precedence; + } + + /** + * Check if a character is an allowed operator char + * + * @param ch the char to check + * @return true if the char is allowed an an operator symbol, false otherwise + */ + public static boolean isAllowedOperatorChar(char ch) { + for (char allowed : ALLOWED_OPERATOR_CHARS) { + if (ch == allowed) { + return true; + } + } + return false; + } + + /** + * Check if the operator is left associative + * + * @return true os the operator is left associative, false otherwise + */ + public boolean isLeftAssociative() { + return leftAssociative; + } + + /** + * Check the precedence value for the operator + * + * @return the precedence value + */ + public int getPrecedence() { + return precedence; + } + + /** + * Apply the operation on the given operands + * + * @param args the operands for the operation + * @return the calculated result of the operation + */ + public abstract double apply(Object... args); + + /** + * Get the operator symbol + * + * @return the symbol + */ + public String getSymbol() { + return symbol; + } + + /** + * Get the number of operands + * + * @return the number of operands + */ + public int getNumOperands() { + return numOperands; + } +} diff --git a/src/main/java/net/objecthunter/exp4j/operator/Operators.java b/src/main/java/net/objecthunter/exp4j/operator/Operators.java new file mode 100644 index 0000000..15488bf --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/operator/Operators.java @@ -0,0 +1,143 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.operator; + +import org.apache.commons.lang3.math.NumberUtils; + +public abstract class Operators { + private static final int INDEX_ADDITION = 0; + private static final int INDEX_SUBTRACTION = 1; + private static final int INDEX_MULTIPLICATION = 2; + private static final int INDEX_DIVISION = 3; + private static final int INDEX_POWER = 4; + private static final int INDEX_MODULO = 5; + private static final int INDEX_UNARY_MINUS = 6; + private static final int INDEX_UNARY_PLUS = 7; + + private static final Operator[] BUILT_IN_OPERATORS = new Operator[8]; + + static { + BUILT_IN_OPERATORS[INDEX_ADDITION] = new Operator("+", 2, true, Operator.PRECEDENCE_ADDITION) { + + + @Override + public double apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString()) && NumberUtils.isNumber(args[1].toString())) { + return Double.parseDouble(args[0].toString()) + Double.parseDouble(args[1].toString()); + } + throw new IllegalArgumentException("Only numbers are allowed for addition"); + } + + }; + BUILT_IN_OPERATORS[INDEX_SUBTRACTION] = new Operator("-", 2, true, Operator.PRECEDENCE_ADDITION) { + @Override + public double apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString()) && NumberUtils.isNumber(args[1].toString())) { + return Double.parseDouble(args[0].toString()) - Double.parseDouble(args[1].toString()); + } + throw new IllegalArgumentException("Only numbers are allowed for addition"); + } + + }; + BUILT_IN_OPERATORS[INDEX_UNARY_MINUS] = new Operator("-", 1, false, Operator.PRECEDENCE_UNARY_MINUS) { + @Override + public double apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())) { + return -Double.parseDouble(args[0].toString()); + } + throw new IllegalArgumentException("Only numbers are allowed for addition"); + } + + }; + BUILT_IN_OPERATORS[INDEX_UNARY_PLUS] = new Operator("+", 1, false, Operator.PRECEDENCE_UNARY_PLUS) { + @Override + public double apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString())) { + return Double.parseDouble(args[0].toString()); + } + throw new IllegalArgumentException("Only numbers are allowed for addition"); + } + + }; + BUILT_IN_OPERATORS[INDEX_MULTIPLICATION] = new Operator("*", 2, true, Operator.PRECEDENCE_MULTIPLICATION) { + @Override + public double apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString()) && NumberUtils.isNumber(args[1].toString())) { + return Double.parseDouble(args[0].toString()) * Double.parseDouble(args[1].toString()); + } + throw new IllegalArgumentException("Only numbers are allowed for addition"); + } + }; + BUILT_IN_OPERATORS[INDEX_DIVISION] = new Operator("/", 2, true, Operator.PRECEDENCE_DIVISION) { + @Override + public double apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString()) && NumberUtils.isNumber(args[1].toString())) { + return Double.parseDouble(args[0].toString()) / Double.parseDouble(args[1].toString()); + } + throw new IllegalArgumentException("Only numbers are allowed for addition"); + } + + }; + BUILT_IN_OPERATORS[INDEX_POWER] = new Operator("^", 2, false, Operator.PRECEDENCE_POWER) { + @Override + public double apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString()) && NumberUtils.isNumber(args[1].toString())) { + return Math.pow(Double.parseDouble(args[0].toString()), Double.parseDouble(args[1].toString())); + } + throw new IllegalArgumentException("Only numbers are allowed for addition"); + } + + }; + BUILT_IN_OPERATORS[INDEX_MODULO] = new Operator("%", 2, true, Operator.PRECEDENCE_MODULO) { + @Override + public double apply(Object... args) { + if (NumberUtils.isNumber(args[0].toString()) && NumberUtils.isNumber(args[1].toString())) { + return Double.parseDouble(args[0].toString()) % Double.parseDouble(args[1].toString()); + } + throw new IllegalArgumentException("Only numbers are allowed for addition"); + } + }; + } + + public static Operator getBuiltinOperator(final char symbol, final int numArguments) { + switch (symbol) { + case '+': + if (numArguments != 1) { + return BUILT_IN_OPERATORS[INDEX_ADDITION]; + } + + return BUILT_IN_OPERATORS[INDEX_UNARY_PLUS]; + case '-': + if (numArguments != 1) { + return BUILT_IN_OPERATORS[INDEX_SUBTRACTION]; + } + + return BUILT_IN_OPERATORS[INDEX_UNARY_MINUS]; + case '*': + return BUILT_IN_OPERATORS[INDEX_MULTIPLICATION]; + case '÷': + case '/': + return BUILT_IN_OPERATORS[INDEX_DIVISION]; + case '^': + return BUILT_IN_OPERATORS[INDEX_POWER]; + case '%': + return BUILT_IN_OPERATORS[INDEX_MODULO]; + default: + return null; + } + } + +} diff --git a/src/main/java/net/objecthunter/exp4j/shuntingyard/ShuntingYard.java b/src/main/java/net/objecthunter/exp4j/shuntingyard/ShuntingYard.java new file mode 100644 index 0000000..9795123 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/shuntingyard/ShuntingYard.java @@ -0,0 +1,106 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.shuntingyard; + +import net.objecthunter.exp4j.operator.Operator; +import net.objecthunter.exp4j.tokenizer.OperatorToken; +import net.objecthunter.exp4j.function.Function; +import net.objecthunter.exp4j.tokenizer.Token; +import net.objecthunter.exp4j.tokenizer.Tokenizer; + +import java.util.*; + +/** + * Shunting yard implementation to convert infix to reverse polish notation + */ +public class ShuntingYard { + + /** + * Convert a Set of tokens from infix to reverse polish notation + * + * @param expression the expression to convert + * @param userFunctions the custom functions used + * @param userOperators the custom operators used + * @param variableNames the variable names used in the expression + * @param implicitMultiplication set to false to turn off implicit multiplication + * @return a {@link Token} array containing the result + */ + public static Token[] convertToRPN(final String expression, final Map userFunctions, + final Map userOperators, final Set variableNames, final boolean implicitMultiplication) { + final Stack stack = new Stack<>(); + final List output = new ArrayList<>(); + + final Tokenizer tokenizer = new Tokenizer(expression, userFunctions, userOperators, variableNames, implicitMultiplication); + while (tokenizer.hasNext()) { + Token token = tokenizer.nextToken(); + switch (token.getType()) { + case Token.TOKEN_NUMBER: + case Token.TOKEN_VARIABLE: + output.add(token); + break; + case Token.TOKEN_FUNCTION: + stack.add(token); + break; + case Token.TOKEN_SEPARATOR: + while (!stack.empty() && stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN) { + output.add(stack.pop()); + } + if (stack.empty() || stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN) { + throw new IllegalArgumentException("Misplaced function separator ',' or mismatched parentheses"); + } + break; + case Token.TOKEN_OPERATOR: + while (!stack.empty() && stack.peek().getType() == Token.TOKEN_OPERATOR) { + OperatorToken o1 = (OperatorToken) token; + OperatorToken o2 = (OperatorToken) stack.peek(); + if (o1.getOperator().getNumOperands() == 1 && o2.getOperator().getNumOperands() == 2) { + break; + } else if ((o1.getOperator().isLeftAssociative() && o1.getOperator().getPrecedence() <= o2.getOperator().getPrecedence()) + || (o1.getOperator().getPrecedence() < o2.getOperator().getPrecedence())) { + output.add(stack.pop()); + } else { + break; + } + } + stack.push(token); + break; + case Token.TOKEN_PARENTHESES_OPEN: + stack.push(token); + break; + case Token.TOKEN_PARENTHESES_CLOSE: + while (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN) { + output.add(stack.pop()); + } + stack.pop(); + if (!stack.isEmpty() && stack.peek().getType() == Token.TOKEN_FUNCTION) { + output.add(stack.pop()); + } + break; + default: + throw new IllegalArgumentException("Unknown Token type encountered. This should not happen"); + } + } + while (!stack.empty()) { + Token t = stack.pop(); + if (t.getType() == Token.TOKEN_PARENTHESES_CLOSE || t.getType() == Token.TOKEN_PARENTHESES_OPEN) { + throw new IllegalArgumentException("Mismatched parentheses detected. Please check the expression"); + } else { + output.add(t); + } + } + return output.toArray(new Token[0]); + } +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/ArgumentSeparatorToken.java b/src/main/java/net/objecthunter/exp4j/tokenizer/ArgumentSeparatorToken.java new file mode 100644 index 0000000..c4de015 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/ArgumentSeparatorToken.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.tokenizer; + +/** + * Represents an argument separator in functions i.e: ',' + */ +class ArgumentSeparatorToken extends Token { + /** + * Create a new instance + */ + ArgumentSeparatorToken() { + super(Token.TOKEN_SEPARATOR); + } +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/CloseParenthesesToken.java b/src/main/java/net/objecthunter/exp4j/tokenizer/CloseParenthesesToken.java new file mode 100644 index 0000000..ba84ab8 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/CloseParenthesesToken.java @@ -0,0 +1,29 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.tokenizer; + +/** + * Represents closed parentheses + */ +class CloseParenthesesToken extends Token { + + /** + * Create a new instance + */ + CloseParenthesesToken() { + super(TOKEN_PARENTHESES_CLOSE); + } +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/FunctionToken.java b/src/main/java/net/objecthunter/exp4j/tokenizer/FunctionToken.java new file mode 100644 index 0000000..f3ebfc7 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/FunctionToken.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.tokenizer; + +import net.objecthunter.exp4j.function.Function; + +public class FunctionToken extends Token { + private final Function function; + + public FunctionToken(final Function function) { + super(TOKEN_FUNCTION); + this.function = function; + } + + public Function getFunction() { + return function; + } +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/NumberToken.java b/src/main/java/net/objecthunter/exp4j/tokenizer/NumberToken.java new file mode 100644 index 0000000..4feea02 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/NumberToken.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.tokenizer; + +/** + * Represents a number in the expression + */ +public final class NumberToken extends Token { + private final double value; + + /** + * Create a new instance + * + * @param value the value of the number + */ + public NumberToken(double value) { + super(TOKEN_NUMBER); + this.value = value; + } + + NumberToken(final char[] expression, final int offset, final int len) { + this(Double.parseDouble(String.valueOf(expression, offset, len))); + } + + /** + * Get the value of the number + * + * @return the value + */ + public double getValue() { + return value; + } +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/OpenParenthesesToken.java b/src/main/java/net/objecthunter/exp4j/tokenizer/OpenParenthesesToken.java new file mode 100644 index 0000000..6bba769 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/OpenParenthesesToken.java @@ -0,0 +1,23 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.tokenizer; + +class OpenParenthesesToken extends Token { + + OpenParenthesesToken() { + super(TOKEN_PARENTHESES_OPEN); + } +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/OperatorToken.java b/src/main/java/net/objecthunter/exp4j/tokenizer/OperatorToken.java new file mode 100644 index 0000000..de3f15d --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/OperatorToken.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.tokenizer; + +import net.objecthunter.exp4j.operator.Operator; + +/** + * Represents an operator used in expressions + */ +public class OperatorToken extends Token { + private final Operator operator; + + /** + * Create a new instance + * + * @param op the operator + */ + public OperatorToken(Operator op) { + super(TOKEN_OPERATOR); + if (op == null) { + throw new IllegalArgumentException("Operator is unknown for token."); + } + this.operator = op; + } + + /** + * Get the operator for that token + * + * @return the operator + */ + public Operator getOperator() { + return operator; + } +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/Token.java b/src/main/java/net/objecthunter/exp4j/tokenizer/Token.java new file mode 100644 index 0000000..ba28ea7 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/Token.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.tokenizer; + +/** + * Abstract class for tokens used by exp4j to tokenize expressions + */ +public abstract class Token { + public static final short TOKEN_NUMBER = 1; + public static final short TOKEN_OPERATOR = 2; + public static final short TOKEN_FUNCTION = 3; + public static final short TOKEN_PARENTHESES_OPEN = 4; + public static final short TOKEN_PARENTHESES_CLOSE = 5; + public static final short TOKEN_VARIABLE = 6; + public static final short TOKEN_SEPARATOR = 7; + + private final int type; + + Token(int type) { + this.type = type; + } + + public int getType() { + return type; + } + +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/Tokenizer.java b/src/main/java/net/objecthunter/exp4j/tokenizer/Tokenizer.java new file mode 100644 index 0000000..54f8dc4 --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/Tokenizer.java @@ -0,0 +1,290 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.tokenizer; + +import net.objecthunter.exp4j.function.Functions; +import net.objecthunter.exp4j.operator.Operator; +import net.objecthunter.exp4j.function.Function; +import net.objecthunter.exp4j.operator.Operators; + +import java.util.Map; +import java.util.Set; + +public class Tokenizer { + + private final char[] expression; + + private final int expressionLength; + + private final Map userFunctions; + + private final Map userOperators; + + private final Set variableNames; + + private final boolean implicitMultiplication; + + private int pos = 0; + + private Token lastToken; + + + public Tokenizer(String expression, final Map userFunctions, + final Map userOperators, final Set variableNames, final boolean implicitMultiplication) { + this.expression = expression.trim().toCharArray(); + this.expressionLength = this.expression.length; + this.userFunctions = userFunctions; + this.userOperators = userOperators; + this.variableNames = variableNames; + this.implicitMultiplication = implicitMultiplication; + } + + public Tokenizer(String expression, final Map userFunctions, + final Map userOperators, final Set variableNames) { + this.expression = expression.trim().toCharArray(); + this.expressionLength = this.expression.length; + this.userFunctions = userFunctions; + this.userOperators = userOperators; + this.variableNames = variableNames; + this.implicitMultiplication = true; + } + + public boolean hasNext() { + return this.expression.length > pos; + } + + public Token nextToken() { + char ch = expression[pos]; + while (Character.isWhitespace(ch)) { + ch = expression[++pos]; + } + if (Character.isDigit(ch) || ch == '.') { + if (lastToken != null) { + if (lastToken.getType() == Token.TOKEN_NUMBER) { + throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]"); + } else if (implicitMultiplication && (lastToken.getType() != Token.TOKEN_OPERATOR + && lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN + && lastToken.getType() != Token.TOKEN_FUNCTION + && lastToken.getType() != Token.TOKEN_SEPARATOR)) { + // insert an implicit multiplication token + lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2)); + return lastToken; + } + } + return parseNumberToken(ch); + } else if (isArgumentSeparator(ch)) { + return parseArgumentSeparatorToken(); + } else if (isOpenParentheses(ch)) { + if (lastToken != null && implicitMultiplication && + (lastToken.getType() != Token.TOKEN_OPERATOR + && lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN + && lastToken.getType() != Token.TOKEN_FUNCTION + && lastToken.getType() != Token.TOKEN_SEPARATOR)) { + // insert an implicit multiplication token + lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2)); + return lastToken; + } + return parseParentheses(true); + } else if (isCloseParentheses(ch)) { + return parseParentheses(false); + } else if (Operator.isAllowedOperatorChar(ch)) { + return parseOperatorToken(ch); + } else if (isAlphabetic(ch) || ch == '_') { + // parse the name which can be a setVariable or a function + if (lastToken != null && implicitMultiplication && + (lastToken.getType() != Token.TOKEN_OPERATOR + && lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN + && lastToken.getType() != Token.TOKEN_FUNCTION + && lastToken.getType() != Token.TOKEN_SEPARATOR)) { + // insert an implicit multiplication token + lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2)); + return lastToken; + } + return parseFunctionOrVariable(); + + } + throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]"); + } + + private Token parseArgumentSeparatorToken() { + this.pos++; + this.lastToken = new ArgumentSeparatorToken(); + return lastToken; + } + + private boolean isArgumentSeparator(char ch) { + return ch == ','; + } + + private Token parseParentheses(final boolean open) { + if (open) { + this.lastToken = new OpenParenthesesToken(); + } else { + this.lastToken = new CloseParenthesesToken(); + } + this.pos++; + return lastToken; + } + + private boolean isOpenParentheses(char ch) { + return ch == '(' || ch == '{' || ch == '['; + } + + private boolean isCloseParentheses(char ch) { + return ch == ')' || ch == '}' || ch == ']'; + } + + private Token parseFunctionOrVariable() { + final int offset = this.pos; + int testPos; + int lastValidLen = 1; + Token lastValidToken = null; + int len = 1; + if (isEndOfExpression(offset)) { + this.pos++; + } + testPos = offset + len - 1; + while (!isEndOfExpression(testPos) && + isVariableOrFunctionCharacter(expression[testPos])) { + String name = new String(expression, offset, len); + if (variableNames != null && variableNames.contains(name)) { + lastValidLen = len; + lastValidToken = new VariableToken(name); + } else { + final Function f = getFunction(name); + if (f != null) { + lastValidLen = len; + lastValidToken = new FunctionToken(f); + } + } + len++; + testPos = offset + len - 1; + } + if (lastValidToken == null) { + throw new UnknownFunctionOrVariableException(new String(expression), pos, len); + } + pos += lastValidLen; + lastToken = lastValidToken; + return lastToken; + } + + private Function getFunction(String name) { + Function f = null; + if (this.userFunctions != null) { + f = this.userFunctions.get(name); + } + if (f == null) { + f = Functions.getBuiltinFunction(name); + } + return f; + } + + private Token parseOperatorToken(char firstChar) { + final int offset = this.pos; + int len = 1; + final StringBuilder symbol = new StringBuilder(); + Operator lastValid = null; + symbol.append(firstChar); + + while (!isEndOfExpression(offset + len) && Operator.isAllowedOperatorChar(expression[offset + len])) { + symbol.append(expression[offset + len++]); + } + + while (symbol.length() > 0) { + Operator op = this.getOperator(symbol.toString()); + if (op == null) { + symbol.setLength(symbol.length() - 1); + } else { + lastValid = op; + break; + } + } + + pos += symbol.length(); + lastToken = new OperatorToken(lastValid); + return lastToken; + } + + private Operator getOperator(String symbol) { + Operator op = null; + if (this.userOperators != null) { + op = this.userOperators.get(symbol); + } + if (op == null && symbol.length() == 1) { + int argc = 2; + if (lastToken == null) { + argc = 1; + } else { + int lastTokenType = lastToken.getType(); + if (lastTokenType == Token.TOKEN_PARENTHESES_OPEN || lastTokenType == Token.TOKEN_SEPARATOR) { + argc = 1; + } else if (lastTokenType == Token.TOKEN_OPERATOR) { + final Operator lastOp = ((OperatorToken) lastToken).getOperator(); + if (lastOp.getNumOperands() == 2 || (lastOp.getNumOperands() == 1 && !lastOp.isLeftAssociative())) { + argc = 1; + } + } + + } + op = Operators.getBuiltinOperator(symbol.charAt(0), argc); + } + return op; + } + + private Token parseNumberToken(final char firstChar) { + final int offset = this.pos; + int len = 1; + this.pos++; + if (isEndOfExpression(offset + len)) { + lastToken = new NumberToken(Double.parseDouble(String.valueOf(firstChar))); + return lastToken; + } + while (!isEndOfExpression(offset + len) && + isNumeric(expression[offset + len], expression[offset + len - 1] == 'e' || + expression[offset + len - 1] == 'E')) { + len++; + this.pos++; + } + // check if the e is at the end + if (expression[offset + len - 1] == 'e' || expression[offset + len - 1] == 'E') { + // since the e is at the end it's not part of the number and a rollback is necessary + len--; + pos--; + } + lastToken = new NumberToken(expression, offset, len); + return lastToken; + } + + private static boolean isNumeric(char ch, boolean lastCharE) { + return Character.isDigit(ch) || ch == '.' || ch == 'e' || ch == 'E' || + (lastCharE && (ch == '-' || ch == '+')); + } + + public static boolean isAlphabetic(int codePoint) { + return Character.isLetter(codePoint); + } + + public static boolean isVariableOrFunctionCharacter(int codePoint) { + return isAlphabetic(codePoint) || + Character.isDigit(codePoint) || + codePoint == '_' || + codePoint == '.'; + } + + private boolean isEndOfExpression(int offset) { + return this.expressionLength <= offset; + } +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/UnknownFunctionOrVariableException.java b/src/main/java/net/objecthunter/exp4j/tokenizer/UnknownFunctionOrVariableException.java new file mode 100644 index 0000000..f01be1f --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/UnknownFunctionOrVariableException.java @@ -0,0 +1,64 @@ +package net.objecthunter.exp4j.tokenizer; + +/** + * This exception is being thrown whenever {@link Tokenizer} finds unknown function or variable. + * + * @author Bartosz Firyn (sarxos) + */ +class UnknownFunctionOrVariableException extends IllegalArgumentException { + + /** + * Serial version UID. + */ + private static final long serialVersionUID = 1L; + + private final String message; + private final String expression; + private final String token; + private final int position; + + public UnknownFunctionOrVariableException(String expression, int position, int length) { + this.expression = expression; + this.token = token(expression, position, length); + this.position = position; + this.message = "Unknown function or variable '" + token + "' at pos " + position + " in expression '" + expression + "'"; + } + + private static String token(String expression, int position, int length) { + + int len = expression.length(); + int end = position + length - 1; + + if (len < end) { + end = len; + } + + return expression.substring(position, end); + } + + @Override + public String getMessage() { + return message; + } + + /** + * @return Expression which contains unknown function or variable + */ + public String getExpression() { + return expression; + } + + /** + * @return The name of unknown function or variable + */ + public String getToken() { + return token; + } + + /** + * @return The position of unknown function or variable + */ + public int getPosition() { + return position; + } +} diff --git a/src/main/java/net/objecthunter/exp4j/tokenizer/VariableToken.java b/src/main/java/net/objecthunter/exp4j/tokenizer/VariableToken.java new file mode 100644 index 0000000..8621b2d --- /dev/null +++ b/src/main/java/net/objecthunter/exp4j/tokenizer/VariableToken.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014 Frank Asseg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.objecthunter.exp4j.tokenizer; + +/** + * represents a setVariable used in an expression + */ +public class VariableToken extends Token { + private final String name; + + /** + * Get the name of the setVariable + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Create a new instance + * + * @param name the name of the setVariable + */ + public VariableToken(String name) { + super(TOKEN_VARIABLE); + this.name = name; + } +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..1df69a7 --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,104 @@ + + + +## ???? + +#spring.datasource.url=${url:jdbc:mysql://rm-bp11k2zm2fr7864428o.mysql.rds.aliyuncs.com/data_center_aeon_admin} +#spring.datasource.username=${username:zhc} +#spring.datasource.password=${password:Youqu48bnb1} +#spring.datasource.driver-class-name=com.mysql.jdbc.Driver +#spring.datasource.hikari.driver-class-name=com.mysql.jdbc.Driver +#spring.datasource.hikari.schema=data_center + + +spring.datasource.admin.name=${springApplicationName:sender} +spring.datasource.admin.url=${jdbcUrl:jdbc:mysql://rm-bp11k2zm2fr7864428o.mysql.rds.aliyuncs.com/data_center_admin} +spring.datasource.admin.username=${jdbcUsername:zhc} +spring.datasource.admin.password=${jdbcPassword:Youqu48bnb1} +spring.datasource.admin.driverClassName=com.mysql.jdbc.Driver +spring.datasource.admin.hikari.driverClassName=com.mysql.jdbc.Driver +spring.datasource.admin.hikari.schema=data_center_aeon_admin +spring.datasource.admin.hikari.minimum-idle: 5 +spring.datasource.admin.hikari.maximum-pool-size: ${rdsMaxPool:40} +spring.datasource.admin.hikari.connection-timeout:10000 + +dynamic.jdbc.url=${dynamicJdbcUrl:jdbc:mysql://rm-bp11k2zm2fr7864428o.mysql.rds.aliyuncs.com/%s} + + +spring.redis.host=r-uf63x4g5p6ir5xao87pd.redis.rds.aliyuncs.com +spring.redis.password=B2BGn4gK4htgkEwP +spring.redis.port=6379 +spring.redis.database=0 +spring.redis.timeout=1000 +spring.redis.lettuce.pool.max-active=8 +spring.redis.lettuce.pool.min-idle=0 +spring.redis.lettuce.pool.max-idle=10 +spring.redis.lettuce.pool.max-wait=1000 +spring.redis.lettuce.shutdown-timeout=1000 + +redis.lock.expire=1000 + + +#data.center.sender.url=http://localhost:8033/nesic/deviceId/ +data.center.sender.url=${dataCenterSenderUrl:http://localhost:8033/nesic/deviceId/} + +amazon.aws.accesskey=${awsaccesskey:AKIA5OFH5OOZHM3U3KX4} +amazon.aws.secretkey=${secretkey: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} + +amazon.aws.alert.topic=${mqttAlertTopic:alertTopic} +amazon.aws.normal.topic=${mqttNormalTopic:normalTopic} + + +alarm.redis.host=${redisHost:r-uf63x4g5p6ir5xao87pd.redis.rds.aliyuncs.com} +alarm.redis.port=${redisPort:6379} +alarm.redis.password=${redisPassword:B2BGn4gK4htgkEwP} +alarm.redis.database=${alarmRedisDatabase:0} + + + + + +roid.alarm.url=${roidAlarmUrl:http://localhost:8080} +roid.alarm.cancel.url=${roidAlarmCancelUrl:} +roid.authorization=${roidAuthorization:} + + +roid2.authorization=${roid2Authorization:Bearer} + +roid2.url=${roid2Url:https://api.public-api.kanri-roid.app/api/public/v1/targets/{targetId}/monitoring-status} + + +alarm.receiver=${alarmReceiver:} + +email.restful.url=${emailRestfulUrl:http://localhost:8080} + +query.push.info=${queryPushInfo:http://localhost:20008/api/targetConfig/config/v2/queryAlertForwardConfigByDeviceId} + +business.query.push.info=${businessQueryPushInfoUrl:} + +alarm.email.result.url=${alarmEmailResultUrl:} + +roidBaStatus.url=${roidBaStatusUrl:/api/public/v1/targets/{targetId}/running-status} + +iotcore.endpoint=${iotcoreEndpoint:iotcore-mqtts-stg.ttkdatatechbuild.com} +iotcore.port=${iotcorePort:8883} +iotcore.clientId=${iotcoreClientId:tkbuild-demo} +iotcore.env=${iotcoreEnv:dev} + +server.tomcat.threads.max=500 +server.tomcat.threads.min-spare=20 + +# 报警类设备类型ID +category.alarm.deviceTypeIds=46,110 +# 计测类设备类型ID +category.measure.deviceTypeIds=47,111,121 +# 累积类设备类型ID +category.accumulate.deviceTypeIds=48,112,122 +# 状态类设备类型ID +category.status.deviceTypeIds=86,113,123 + +sys.mqtt.endpoint=${sysMqttEndpoint:mqtt-stg.kr-sensor.net} +sys.mqtt.port=${sysMqttPort:1883} +sys.mqtt.username=${sysMqttUsername:test} +sys.mqtt.password=${sysMqttPassword:test} +sys.mqtt.clientId=${sysMqttClientId:sys-mqtt-client} diff --git a/src/main/resources/application-prd.properties b/src/main/resources/application-prd.properties new file mode 100644 index 0000000..790b88b --- /dev/null +++ b/src/main/resources/application-prd.properties @@ -0,0 +1,98 @@ + +## ???? +#spring.datasource.url=${jdbcUrl} +#spring.datasource.username=${jdbcUsername} +#spring.datasource.password=${jdbcPassword} +#spring.datasource.driver-class-name=com.mysql.jdbc.Driver +#spring.datasource.hikari.driver-class-name=com.mysql.jdbc.Driver +#spring.datasource.hikari.schema=data_center +#spring.datasource.hikari.minimum-idle: 5 +#spring.datasource.hikari.maximum-pool-size: ${rdsMaxPool:40} +#spring.datasource.hikari.connection-timeout:10000 + +spring.datasource.admin.name=${springApplicationName:sender} +spring.datasource.admin.url=${jdbcUrl:jdbc:mysql://rm-bp11k2zm2fr7864428o.mysql.rds.aliyuncs.com/data_center_admin} +spring.datasource.admin.username=${jdbcUsername:zhc} +spring.datasource.admin.password=${jdbcPassword:Youqu48bnb1} +spring.datasource.admin.driverClassName=com.mysql.jdbc.Driver +spring.datasource.admin.hikari.driverClassName=com.mysql.jdbc.Driver +spring.datasource.admin.hikari.schema=data_center_aeon_admin +spring.datasource.admin.hikari.minimum-idle: 5 +spring.datasource.admin.hikari.maximum-pool-size: ${rdsMaxPool:40} +spring.datasource.admin.hikari.connection-timeout:10000 + +dynamic.jdbc.url=${dynamicJdbcUrl:jdbc:mysql://rm-bp11k2zm2fr7864428o.mysql.rds.aliyuncs.com/%s} + + +spring.redis.host=${redisHost} +spring.redis.password=${redisPassword} +spring.redis.port=${redisPort} +spring.redis.database=${redisDatabase} +spring.redis.timeout=${redisTimeout} +spring.redis.lettuce.pool.max-active=${redisMaxActive} +spring.redis.lettuce.pool.min-idle=${redisMinIdle} +spring.redis.lettuce.pool.max-idle=${redisMaxIdle} +spring.redis.lettuce.pool.max-wait=${redisMaxWait} +spring.redis.lettuce.shutdown-timeout=${redisShutdownTimeout} + +redis.lock.expire=${redisLockExpire:1000} + +data.center.sender.url=${dataCenterSenderTargetUrl} + + +amazon.aws.accesskey=${awsaccesskey:AKIAVRXFMB43XVQ3GXAL} +amazon.aws.secretkey=${secretkey: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} + +amazon.aws.alert.topic=${mqttAlertTopic:alertTopic} +amazon.aws.normal.topic=${mqttNormalTopic:normalTopic} + +alarm.redis.host=${redisHost:localhost} +alarm.redis.port=${redisPort:6379} +alarm.redis.password=${redisPassword:} +alarm.redis.database=${alarmRedisDatabase:0} + + + +roid.alarm.url=${roidAlarmUrl:http://localhost:8080} +roid.alarm.cancel.url=${roidAlarmCancelUrl:} + +roid.authorization=${roidAuthorization:} + +roid2.authorization=${roid2Authorization:Bearer} + +roid2.url=${roid2Url:https://api.public-api.kanri-roid.app/api/public/v1/targets/{targetId}/monitoring-status} + +alarm.receiver=${alarmReceiver:} + +email.restful.url=${emailRestfulUrl:http://localhost:8080} + +query.push.info=${queryPushInfoUrl:} + +business.query.push.info=${businessQueryPushInfoUrl:} + +alarm.email.result.url=${alarmEmailResultUrl:} + +roidBaStatus.url=${roidBaStatusUrl:} + +iotcore.endpoint=${iotcoreEndpoint:a6lbhgp873vur-ats.iot.ap-northeast-1.amazonaws.com} +iotcore.port=${iotcorePort:8883} +iotcore.clientId=${iotcoreClientId:basicPubSub} +iotcore.env=${iotcoreEnv:prd} + +server.tomcat.threads.max=500 +server.tomcat.threads.min-spare=20 + +# 报警类设备类型ID +category.alarm.deviceTypeIds=46,110 +# 计测类设备类型ID +category.measure.deviceTypeIds=47,111,121 +# 累积类设备类型ID +category.accumulate.deviceTypeIds=48,112,122 +# 状态类设备类型ID +category.status.deviceTypeIds=86,113,123 + +sys.mqtt.endpoint=${sysMqttEndpoint:mqtt-stg.kr-sensor.net} +sys.mqtt.port=${sysMqttPort:1883} +sys.mqtt.username=${sysMqttUsername:test} +sys.mqtt.password=${sysMqttPassword:test} +sys.mqtt.clientId=${sysMqttClientId:sys-mqtt-client} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..b96cbcf --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,20 @@ +server.port=8201 + +spring.profiles.active=${env:dev} + +spring.application.name=data-center-sender + +ok.http.connect-timeout=${okHttpConnectTimeout:6000} +ok.http.read-timeout=${okHttpConnectTimeoutt:6000} +ok.http.write-timeout=${okHttpConnectTimeout:6000} +ok.http.max-idle-connections=${okHttpMaxIdleConnections:100} +ok.http.keep-alive-duration=${okHttpKeepAliveDuration:300} + +# ???1?? (?????) +spring.cache.redis.time-to-live=${defaultRedisCacheTTL:300000} +management.endpoints.web.exposure.include=metrics + +uid.enable=true + + + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..54a6756 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,49 @@ + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{requestId}] - %msg%n + + + + + + + + ./logs/sender.log + + + ./logs/sender.%d{yyyy-MM-dd}.log + + 7 + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{requestId}] - %msg%n + UTF-8 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/ssl/dev/iotcore.ca.crt b/src/main/resources/ssl/dev/iotcore.ca.crt new file mode 100644 index 0000000..61ae256 --- /dev/null +++ b/src/main/resources/ssl/dev/iotcore.ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/src/main/resources/ssl/dev/iotcore.cert.pem b/src/main/resources/ssl/dev/iotcore.cert.pem new file mode 100644 index 0000000..843ddca --- /dev/null +++ b/src/main/resources/ssl/dev/iotcore.cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWTCCAkGgAwIBAgIUbKRo9G99LmScRih1wcB4DQdzzycwDQYJKoZIhvcNAQEL +BQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g +SW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTI0MTAzMDA3MTI1 +OVoXDTQ5MTIzMTIzNTk1OVowHjEcMBoGA1UEAwwTQVdTIElvVCBDZXJ0aWZpY2F0 +ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK1URg/NtZv3RvW6glG6 +Eff989Bro8eHScPC6Xm2jDR+LoOw4vocAcpcotLFz9OPUReo8llFm5yzOFBFDMef +/+sgBbzhpi4q2Vt6RAXeW5R7MYdpDRKfwk0mtLDMtO+8lbb+g2J9VWX2qYsxuVkZ +pHL94SyCldIe1GzaGCsSwFhRgMw6zi+WQ5HPicHqzy3h7fUWRCAGmTXOvj+Xuuzm +KNCCs6ylBsxhisPnMWLhzIeRdgk+bFQnSrT8TcYjbh0sqVyKeFHPsMDymoBmSTNZ +UrrhZdRNK4iR8UEKtr7sKdJYz2nyCOgLGzCOcJckx4yDtNJC/7RxfaEzeSikbxI2 +g88CAwEAAaNgMF4wHwYDVR0jBBgwFoAUfioxTfpGWqCkvtB59f773Q8o4lEwHQYD +VR0OBBYEFGVIkg1PzIECp/1rKMSCu/AsGjMUMAwGA1UdEwEB/wQCMAAwDgYDVR0P +AQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQBnH1+iIy56KLlJ2WiJIi6yuml5 +udjslX5Rnye3A935cUuSFjH6hdFa+dbjzC81qlkvldlxTh2bB1RUCNQtAXc+NTO7 +/VadFy/oSeOLIiu5rcWqvmGwLCl5PIOUg8D2ncalqE4vZxYAaRhAGYp3v4v6L5RE +LrcpDB48/9A4yuTUsSCEZyP1Kd4sg67S/RHEBO8Abc1HyqaHmGOjdmOtRi0mktp6 +meQJrirrXlDL8ayutHfp6Me+thKDH040UUIILUHQKHZlvjlTitjhtZW9VwUJZuzE +8fFKy7Y0TowXUzR/z6RpCilmuMmRHju+Go0PnQFD22BUllhSBGQB0PXiQ6qy +-----END CERTIFICATE----- diff --git a/src/main/resources/ssl/dev/iotcore.private.key b/src/main/resources/ssl/dev/iotcore.private.key new file mode 100644 index 0000000..4c9025d --- /dev/null +++ b/src/main/resources/ssl/dev/iotcore.private.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEArVRGD821m/dG9bqCUboR9/3z0Gujx4dJw8LpebaMNH4ug7Di ++hwBylyi0sXP049RF6jyWUWbnLM4UEUMx5//6yAFvOGmLirZW3pEBd5blHsxh2kN +Ep/CTSa0sMy077yVtv6DYn1VZfapizG5WRmkcv3hLIKV0h7UbNoYKxLAWFGAzDrO +L5ZDkc+JwerPLeHt9RZEIAaZNc6+P5e67OYo0IKzrKUGzGGKw+cxYuHMh5F2CT5s +VCdKtPxNxiNuHSypXIp4Uc+wwPKagGZJM1lSuuFl1E0riJHxQQq2vuwp0ljPafII +6AsbMI5wlyTHjIO00kL/tHF9oTN5KKRvEjaDzwIDAQABAoIBAGoL8qW+pUuceiTk +DNG190J0dNTi1pzAUEhem3xlTNjdDtZxxh1iP1IxZUlxz5rPx7yjGEu4Q+IW4PBz +nsMQSILx37DVmlgKzVujrbSte/iXexEvG9l2N2n+sXSuelFnX25Lv9Tle/dbeBX6 +wT0bbDsSy87wwnuBc1R6B2lrGpWtnk2/8JUSTjlGVj9m22CKa3x4yGf1nEeUDvsj +YWlFbnB+68k8VyYmpTHSaz2zFMvEwET0VDg60h8Bk4ysvagrH8qF6g+y37v094y5 +HOenvwey6FkxOEX3eechxKrjso9Ft/bI4VBYzBw7dHr0qWj5XSl18di7ygvMr12Q +YfXFleECgYEA0ySHpd/MDkkJ2OL8mZ9hDhKbtfWTluTh9leCqi193IfVOyt0+itB +EtH73ilPNyMFiFmhSYp/baIMt3NATdaO3nxE1SXZRR5A0X4yPmCZ6/yfYlSLWwou +m6Jre3dVvltocIN2kZLMeFAFxBX7VabLOIAr3NWggfzJ+5UO1cqfr98CgYEA0icr +6yKZ2ZRplABvj5nyHdc6PQ1a2toqEzeS1+INAfMNdCmDwrtbEd9rXByDvISecX1m +8wptJ9tflLNUcvtBQw3Yuf+0nMp/WcRJ1CDovjvZHaFXp/ULvv+jCBjepr67J3xi +p8BwpKvDId/4mDnWjktQ0yLdZvs6/1cZW+xm6hECgYAXm94PDhcjAce4GKlmBAw2 +Jp6CmhMGrFEX2nEh1jRclFOjahiKYIvGLvSawsO4xpG3zU3bi0D0YUpKuPhXu8X9 +2qdXrgO9VsSoqBO2JdcQLA8mT43FgD316RNbmtxbjJ0A97IqVN/IFCqQ4mRYD25p +wuskpj5sSnfLSb9+6EkywQKBgF9zqVAQlD6uWVH+7K0V6FHDe1FVqomzA99co5bo +tq3G/rpfTNh7Dr9MclARJ4jN18WGhucnOjMbQguhdF8RXjAdru20hg2dMEDOlhYC +qJQXTONXcB/6fk5PgHZiDWdBRHKH3auCDefNmEDfoONLifdxoBF2SYfH3qExp3QI +KSpxAoGAb3RIMLKiXth7+FxCAizq2JDFm0K6c9S/8kC9JzjQtb9P9XNKSvAkx1Du +EWCp4s6CO8UAP9Fw3WZvJx7/1VUoxXxHH9PtGryI+XT1a8WEkN7KvGIVuahh2pq7 +RD8QOVAIi0j3ItMoqZjOxhR4zF0XAMyAE3Grzee+mwd5PMoD9r4= +-----END RSA PRIVATE KEY----- diff --git a/src/main/resources/ssl/dev/iotcore.root.pem b/src/main/resources/ssl/dev/iotcore.root.pem new file mode 100644 index 0000000..66fd962 --- /dev/null +++ b/src/main/resources/ssl/dev/iotcore.root.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/src/main/resources/ssl/prd/iotcore.cert.pem b/src/main/resources/ssl/prd/iotcore.cert.pem new file mode 100644 index 0000000..483675c --- /dev/null +++ b/src/main/resources/ssl/prd/iotcore.cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWTCCAkGgAwIBAgIUQxvbS4uTituSRBVCqDJIieCNjY4wDQYJKoZIhvcNAQEL +BQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g +SW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTI0MTEyNTAzMDk1 +MFoXDTQ5MTIzMTIzNTk1OVowHjEcMBoGA1UEAwwTQVdTIElvVCBDZXJ0aWZpY2F0 +ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ3OuOkcrNc7V0xkk5Ye +J4WFj/tR9ZIUTROFdm8k27RfrdJk9xGX1reVawWfnVLqsRUcDxIHf8mY6WcZ5JS2 +8795/L6vpeZ3lACPGS6zVXB88Z8CSc0aaHfoE/PkeiX4eGoDmY1C/PyxdIFW/7lM +MrjXMaEXAPEjvVB6MmZPMQACm4aNKaNy7apmTZtInO65NnEbXROp/DjDUUFX/LBQ +ud+l3bXHdNPw7P4CzSdkeUI54somzInUydMR2DZOin3MM7SgJ0X8iH+t/E7nDHGK +X90JJDVM0YtUOFtx7nSop3vpGNVGVAlSEFy7jU01ux01cBExtVepVAvIwFjvdgws +DhsCAwEAAaNgMF4wHwYDVR0jBBgwFoAUVbdFkcsXF0PZwPILUx3xSCBHLtkwHQYD +VR0OBBYEFO6/L1oHofMPh7VCRu6m9EGrhSHkMAwGA1UdEwEB/wQCMAAwDgYDVR0P +AQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQBYPi7cHl7bt/C7o0q9Yk7XcccV +EX/EKGELOJwEwDB90eAuMPs0O7DfhFXQaY7M+46EcfSew5C/7GzH7ipki5WEXemx +q6LNLQk/Kd05NdxZaIEBpJjw9us3xTarPZpDhTkNx1OmX77BXTfPELxOw6oWFJUE +2zIfyVuhBnfrYUMQLePGs3vqSk4OBgoGb+rIoxow3gGZZlEAxYn80MwD/O7AVi7g +rV3nIBQKjWkjTfroR69uKca1nlgly65c6AAuz2FLFZdOj9od7Rw/N9G76DdsdD2I +n0C/cbGHL0zW28JNk5qrfVISi9jBZYw0VmD/ruHwTp7JjqU7QMr6aMB1Jj0M +-----END CERTIFICATE----- diff --git a/src/main/resources/ssl/prd/iotcore.private.key b/src/main/resources/ssl/prd/iotcore.private.key new file mode 100644 index 0000000..3c0ebd1 --- /dev/null +++ b/src/main/resources/ssl/prd/iotcore.private.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAnc646Rys1ztXTGSTlh4nhYWP+1H1khRNE4V2byTbtF+t0mT3 +EZfWt5VrBZ+dUuqxFRwPEgd/yZjpZxnklLbzv3n8vq+l5neUAI8ZLrNVcHzxnwJJ +zRpod+gT8+R6Jfh4agOZjUL8/LF0gVb/uUwyuNcxoRcA8SO9UHoyZk8xAAKbho0p +o3LtqmZNm0ic7rk2cRtdE6n8OMNRQVf8sFC536Xdtcd00/Ds/gLNJ2R5QjniyibM +idTJ0xHYNk6KfcwztKAnRfyIf638TucMcYpf3QkkNUzRi1Q4W3HudKine+kY1UZU +CVIQXLuNTTW7HTVwETG1V6lUC8jAWO92DCwOGwIDAQABAoIBACiFOD3DOy5Y8mWP +PcZCH+vNC9eAiAnmZJQqpOqNEE0UdFGd00MVNfEV0NE01vS/hpDhD42Vl15wgR4U +K/lzsMrty9Q6zeKLudka5WB0c+/aZUBrGgAsU77WPKlXdXXigyjVm8wF82f/OOct +K6SsyClmODwg6AEDKE1N0zsqAm751Sc/sFq59nJ1NyHob4uxwYd0t26oSGD3g5On +T4xyZtIEnkiQmX3xk7H2wkS4SdVx6vW3eptWBvVNZKECFEjYJ6rXf70kRBr4tUOJ +MGVqyPJ4frRuH91VkduNu8gcwFq430559QVUO4MfLO82VnK/09Dj4p1ew3Sx5QWE +rK9oCsECgYEAz5n1huakCwYxIep36LMMIWK++0AfjPFckXzFLtrJ5QloDuSRaHe0 +NQVwzwvm9SViH5ZxGHpNUlljTOaXWJ6LbCtCDIl9xGBOGTCuwOulClCFsYvtgbzp +YPHkwlmM2wSLm6V43vs9mAmUI2EiT9VTIeaxrP4/C66tN7dQ9G1reCMCgYEAwpj4 +ZQ8LHCOifuMlQ8dPK6yZnXhExmYC0tOucUiQ8ZztqZ6KDVsn8YsAT9aRb7ODek7f +gMgFBzcqlwTjeYKSAfgB4D+wlYW2qDXqGkFEtmeOFDTzqXKxcNyBUQOsQHTKcTWA +2oEhS/lrM2yaql3gn5LdM/XXjvWEx576DbKjtakCgYEAstRGwiwPh0eQL00Qea0y +d9fd+ASmqPyI5vJ50QC7BcbyklGmSD1FSJ8lK9Vu4CGRwvAPACzqSlOhzG6eVMJO +C6G/xZjyc6UZ4W0kPB9BDf+LCwQkVavrxYzZp+kIAuqJcEw8MKhvyHYH2+oiam6y +q/NTfleM631OzT3mSIexGc0CgYEAovsNZ06fJnUt/ontxwOh6llH21xEdcowaTUD +cDuvsbyWTK7Mvvzuyl156GQ0MNKvtntis/BSTcXFdPCSOE3ETcPShmexVLnFzsJB +DLZHf2Hh3EixHuVBed4krJ6f4OgwOXrvLZJjOcdvIoW+UBwKKnwNqhwn6sL071/V +3C0W8ekCgYEAwhmGj+IF5ABqwbJiKkPgKidGqJgGI5/IQbXFn9eqDQVXrwY26sIe +f3Q1A9Ya3vtJVTCFgZj4vVVBStas0l4/t87geGCq3d2SA/7Vcwhu0QW+R1RxuTIx +NJACPM2wdzaZFOhFEVOpD/2y06RZOzQ6yYplQcwnzXsqKjpZ5Ax/SoY= +-----END RSA PRIVATE KEY----- diff --git a/src/main/resources/ssl/prd/iotcore.root.pem b/src/main/resources/ssl/prd/iotcore.root.pem new file mode 100644 index 0000000..a6f3e92 --- /dev/null +++ b/src/main/resources/ssl/prd/iotcore.root.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- diff --git a/src/test/java/com/techsor/datacenter/sender/AwsIotMqttExample.java b/src/test/java/com/techsor/datacenter/sender/AwsIotMqttExample.java new file mode 100644 index 0000000..0e51b9c --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/AwsIotMqttExample.java @@ -0,0 +1,112 @@ +package com.techsor.datacenter.sender; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.eclipse.paho.client.mqttv3.*; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.KeyManagerFactory; +import java.io.FileInputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.Security; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.PrivateKey; +import java.util.Base64; +import java.util.stream.Collectors; + +public class AwsIotMqttExample { + + public static void main(String[] args) { + String brokerUrl = "ssl://a6lbhgp873vur-ats.iot.ap-northeast-1.amazonaws.com:8883"; // 替换为您的AWS IoT Core端点 + String clientId = "basicPubSub"; + String topic = "sdk/test/java"; + String messageContent = "Hello from Java AWS IoT Core!"; + + // 证书文件路径 + String caCertFile = "C:\\Users\\jwy\\Desktop\\TMP\\iotcore\\root-CA.crt"; + String clientCertFile = "C:\\Users\\jwy\\Desktop\\TMP\\iotcore\\tkbuild-thing.cert.pem"; + String privateKeyFile = "C:\\Users\\jwy\\Desktop\\TMP\\iotcore\\tkbuild-thing.private.key"; + + try { + // 加载 BouncyCastle 提供器 + Security.addProvider(new BouncyCastleProvider()); + + // 创建 KeyStore 并加载 CA 证书 + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, null); + FileInputStream fis = new FileInputStream(caCertFile); + X509Certificate caCert = (X509Certificate) cf.generateCertificate(fis); + keyStore.setCertificateEntry("ca-cert", caCert); + + // 加载客户端证书 + fis = new FileInputStream(clientCertFile); + X509Certificate clientCert = (X509Certificate) cf.generateCertificate(fis); + keyStore.setCertificateEntry("client-cert", clientCert); + + // 加载私钥 +// String privateKeyPEM = new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(privateKeyFile))); +// privateKeyPEM = privateKeyPEM +// .replace("-----BEGIN PRIVATE KEY-----", "") +// .replace("-----END PRIVATE KEY-----", "") +// .replaceAll("\\s+", ""); +// byte[] decodedKey = Base64.getDecoder().decode(privateKeyPEM); +// PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey); +// KeyFactory kf = KeyFactory.getInstance("RSA"); +// PrivateKey privateKey = kf.generatePrivate(keySpec); + PrivateKey privateKey = loadPrivateKey(privateKeyFile); + keyStore.setKeyEntry("client-key", privateKey, "".toCharArray(), new java.security.cert.Certificate[]{clientCert}); + + // 设置 KeyManager 和 TrustManager + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, "".toCharArray()); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(keyStore); + + SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + // 创建 MQTT 客户端 + MqttConnectOptions options = new MqttConnectOptions(); + options.setSocketFactory(sslContext.getSocketFactory()); +// options.setSocketFactory(SslUtil.getSocketFactory("/ssl/ca.pem", "/ssl/client.pem", "/ssl/client.key", "")); + options.setCleanSession(true); + + MqttClient client = new MqttClient(brokerUrl, clientId); + client.connect(options); + + // 创建和发布消息 + MqttMessage message = new MqttMessage(messageContent.getBytes()); + message.setQos(1); // 设置QoS,值可为0、1或2 + client.publish(topic, message); + + System.out.println("Message published to AWS IoT Core"); + + client.disconnect(); + System.out.println("Disconnected from AWS IoT Core"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static PrivateKey loadPrivateKey(String filename) throws Exception { + // Read the PEM file + String key = Files.lines(Paths.get(filename)) + .filter(line -> !line.startsWith("-----")) + .collect(Collectors.joining()); + + // Decode the base64 content + byte[] keyBytes = Base64.getDecoder().decode(key); + + // Generate private key + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePrivate(spec); + } +} diff --git a/src/test/java/com/techsor/datacenter/sender/KingIOServer/DataTest.java b/src/test/java/com/techsor/datacenter/sender/KingIOServer/DataTest.java new file mode 100644 index 0000000..b4156a6 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/KingIOServer/DataTest.java @@ -0,0 +1,68 @@ +package com.techsor.datacenter.sender.KingIOServer; + +import com.alibaba.fastjson2.JSON; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.techsor.datacenter.sender.entitiy.kingio.KingIODataItemEntity; +import com.techsor.datacenter.sender.entitiy.kingio.KingIODataModel; +import com.techsor.datacenter.sender.service.KingIOServerService; +import com.techsor.datacenter.sender.utils.TimeUtils; +import org.junit.jupiter.api.Test; +import org.springframework.stereotype.Service; + +import java.lang.reflect.Type; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + + +public class DataTest { + + @Test + public void start() throws Exception { +// List testList = new ArrayList<>(); +// testList.add("test1"); +// testList.add("teset2"); +// String json1 = JSON.toJSONString(testList); +// Type listType = new TypeToken>(){}.getType(); +// List KingiOItemList = new Gson().fromJson(json1,listType); +// System.out.println("end"); + wholeTest(); + } + + //测试解析IOServer数据生成的列表,转成单个存储 + private void testListToItem(String processJson){ + Type listType = new TypeToken>(){}.getType(); + List KingiOItemList = new Gson().fromJson(processJson,listType); + for (KingIODataItemEntity item : KingiOItemList) { + System.out.println(new Gson().toJson(item)); + } + } + + private void testKeep4Dicimal_Start(){ + Object value1 = 21.2342354444524; + Object value2= false; + testKeep4Dicimal(value1); + testKeep4Dicimal(value2); + } + + //测试保留4位小数 + private void testKeep4Dicimal(Object value){ + //Process the number value. keep 4 ecimal places + if (value instanceof Number) { + DecimalFormat df = new DecimalFormat("#.####"); // 保留四位小数的格式 + String formatted = df.format((Number)value); + value = Double.parseDouble(formatted); + } + KingIODataItemEntity item = new KingIODataItemEntity("test", value, "2023-12-21 19:44:00", String.valueOf(192)); + System.out.println(new Gson().toJson(item)); + } + + private void wholeTest() throws Exception { +// String data = "{\"PNs\":{\"1\":\"V\",\"2\":\"T\",\"3\":\"Q\"},\"PVs\":{\"1\":false,\"2\":\"2024-11-13 18:04:40.823\",\"3\":192},\"Objs\":[{\"N\":\"Tag31030232\"},{\"N\":\"Tag31030233\"}]}"; +// KingIOServerService kingIOServerService = new KingIOServerService(); +// kingIOServerService.start(data); + + TimeUtils.kingiOServerTimeToTs("2024-11-16 17:44:26.234"); + } +} diff --git a/src/test/java/com/techsor/datacenter/sender/LoggerExeceptionTests.java b/src/test/java/com/techsor/datacenter/sender/LoggerExeceptionTests.java new file mode 100644 index 0000000..01dadbe --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/LoggerExeceptionTests.java @@ -0,0 +1,34 @@ +package com.techsor.datacenter.sender; + +import com.baidu.fsg.uid.UidGenerator; +import com.google.gson.Gson; + +import com.techsor.datacenter.sender.utils.DateUtils; +import com.techsor.datacenter.sender.entitiy.delta.DeltaNumEntity; +import org.junit.Test; + +import jakarta.annotation.Resource; + +import static org.apache.commons.text.StringEscapeUtils.unescapeJson; + +//@RunWith(SpringJUnit4ClassRunner.class) +//@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = TechsorDataCenterSenderApplication.class) +public class LoggerExeceptionTests { + + + + @Resource + private UidGenerator uidGenerator; + + @Test + public void testUidGenerator(){ + String content = unescapeJson("{\\\"id\\\":\\\"TESTDT02\\\",\\\"type\\\":\\\"Analog\\\",\\\"reportedAt\\\":\\\"2024-07-01T13:50:00+09:00\\\",\\\"value\\\":500}"); +// RAWEntity rawEntity = new Gson().fromJson(content,RAWEntity.class); +// String rawContent = rawEntity.getContent(); + DeltaNumEntity deltaNumEntity = new Gson().fromJson(content,DeltaNumEntity.class); + String receiveTsStr = deltaNumEntity.getReportedAt(); + String receiveTs = DateUtils.parseToTimestamp(receiveTsStr)+""; + System.out.println(receiveTs); + } + +} diff --git a/src/test/java/com/techsor/datacenter/sender/MvelTests.java b/src/test/java/com/techsor/datacenter/sender/MvelTests.java new file mode 100644 index 0000000..12353d7 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/MvelTests.java @@ -0,0 +1,178 @@ +package com.techsor.datacenter.sender; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.techsor.datacenter.sender.compiler.MvelExecutor; +import com.techsor.datacenter.sender.dto.DeviceAlertInfo; +import com.techsor.datacenter.sender.utils.DateUtils; + +import cn.hutool.core.collection.CollectionUtil; + +import com.techsor.datacenter.sender.entitiy.DynamodbEntity; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.stream.Collectors; + +public class MvelTests { + + @Test + public void testMvel(){ + String resp = "{\n" + + " \"code\": 200,\n" + + " \"msg\": \"Success\",\n" + + " \"data\": {\n" + + " \"dataList\": [],\n" + + " \"alertContent\": null,\n" + + " \"alertTitle\": null,\n" + + " \"typeInfo\": {\n" + + " \"id\": 46,\n" + + " \"name\": \"Delta 故障\",\n" + + " \"description\": \"\",\n" + + " \"deviceType\": \"DELTA03\",\n" + + " \"deviceDataSrc\": \"mc-01\",\n" + + " \"originJsonFormat\": \"[{\\\"id\\\":\\\"BA0029\\\",\\\"type\\\":\\\"Boolean\\\",\\\"reportedAt\\\":\\\"2022-12-11T13:50:00+09:00\\\",\\\"value\\\":false}]\",\n" + + " \"expressionMap\": \"{\\\"$.deviceId\\\":\\\"$.id\\\",\\\"$.this\\\":\\\"getDeltaDataString($.this)\\\"}\",\n" + + " \"expressionVariableMap\": \"{\\\"$.id\\\":{\\\"$.id\\\":\\\"\\\"},\\\"getDeltaDataString($.this)\\\":{\\\"$.this\\\":\\\"getDeltaDataString\\\"}}\",\n" + + " \"targetJsonFormat\": \"{\\\"deviceId\\\":\\\"$.id\\\"}\",\n" + + " \"targetForwardId\": 1,\n" + + " \"targetFowardCode\": \"1111111\",\n" + + " \"createdBy\": null,\n" + + " \"createdTimestamp\": null,\n" + + " \"updatedBy\": 1,\n" + + " \"updatedTimestamp\": \"2024-09-12T07:11:30.000+00:00\",\n" + + " \"companyId\": 2,\n" + + " \"spaceId\": null,\n" + + " \"flag\": 0,\n" + + " \"dbmId\": \"[deviceSn]_delta_failure\",\n" + + " \"nameZh\": \"Delta 故障\",\n" + + " \"nameEn\": \"Delta 故障\",\n" + + " \"deviceCategoryId\": 2,\n" + + " \"unit\": \"ON-1/OFF-0\"\n" + + " },\n" + + " \"deviceAlertConfig\": null,\n" + + " \"parsedAlarmInfoList\": [\n" + + " {\n" + + " \"alertContent\": \"不具合チケット自動発行システムからの報告です。\\n\\n発生日:2025-04-03 20:07:35\\n物件名:大手町タワー\\n部屋名:スパネイル店舗\\n設備名:スパネイル店舗\\nセンサーID:FT000001\\n不具合内容:QWE2\\nvalue: {Value}\",\n" + + " \"alertTitle\": \"告警触发\",\n" + + " \"alertRecipientMail\": \"\",\n" + + " \"alertRecipientSms\": \"\",\n" + + " \"forwardType\": \"roid1\",\n" + + " \"effectivePeriod\": [\n" + + " \"00:00-23:59\"\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"allEffectivePeriod\": [\n" + + " \"00:00-23:59\"\n" + + " ],\n" + + " \"bearerToken\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIzIiwianRpIjoiYjgxZGM1YTU0ZGNhMjZiYzRiMzY4NzI3NjhmYThjYTVlNWFjNGFiY2VjYjAyMDU5YzYxNjRlOGI1OTg3NWJhNGYyMGE0YWM4YTkzZDc2NWQiLCJpYXQiOjE3Mjc0MTA4MDEuOTQwMzk1LCJuYmYiOjE3Mjc0MTA4MDEuOTQwMzk4LCJleHAiOjQ4ODMwODQ0MDEuOTMxNjExLCJzdWIiOiIyODcxNSIsInNjb3BlcyI6W119.IgjzwrJM8CwUGDPUf0lXPypn1yabmYKT5hrwrskHn7oenOLXzoZC_e3LhrGTEvjsEjj-IRZRemeoIE3wIJ7nxzU_zMnLOed9pYZNFhsN1uHjL8DSrsNyt3MMwM7QPXLVEGKt_bftHEgJexCRMaqOIgXx_VGEHeiTtrLepKlikRDpkQYYE9DvxNZvTQHCUAod51NX5r-dZHKHJmWFigrl7M_IkTThEKnuXMjtAc38hM0yO-u38Nf9ZCMV5SIvYWUe7GRSiyn4uBm-ok_oqoVY0qO63PjQiKB3rr7TAeLkSPuOPN4mS6RKbcMsE9Z5OHmZ1vqFCyMVkv53xjYJzsKPdQDArKl1YIHeieB27bYdswIZ8rkRzbXN3b-cXy4hq1OcGd3BA_2kL_Z7rEmJCPLH5EsRSNJztW3dARQANqfbRKXHDSZrqJMavZG5D5GF7xBS9vMS-y0joFI7gZO5oqO_3Y7HY_TAP2Ly8PggDzHJHV1LLDuI4cLjBSJ1j9hZe3iT9l7BHDnWjwmyTCzyhoIwd9vOIi1MIQnEdJfN-BUekS3-wz1O64pZNPW9jmHPa-tw2zUL5dBjeY2CuMgvY2EE1YB2JEiRuJGna-NpflqlTfTxiJWr7-4MuM9520JH1ciINfFqQSuWNJUrKRisz8noQPTcUyD7isznKZH49X4l-AE\",\n" + + " \"thirdApiHost\": \"https://api-sec.test-public-api.kanri-roid.app\"\n" + + " }\n" + + "}\n"; + JSONObject alertTemplateInfo = JSONObject.parseObject(resp); + + JSONArray parsedAlarmInfoListArray = alertTemplateInfo.getJSONObject("data").getJSONArray("parsedAlarmInfoList"); + JSONObject jsonObject = JSON.parseObject("{\"aaa_test\":1}"); + List valueList = new ArrayList<>(); + for (String key : jsonObject.keySet()) { + valueList.add(jsonObject.get(key)); + } + String pureValue = org.apache.commons.lang3.StringUtils.join(valueList, ","); + for (int k = 0; k < parsedAlarmInfoListArray.size(); k++) { + // 确保元素是 JSONObject 类型(避免 ClassCastException)[[7]] + // 替换每个数组对象中的alertContent中的Value变量为实际值 + if (parsedAlarmInfoListArray.get(k) instanceof JSONObject) { + JSONObject alarmInfo = parsedAlarmInfoListArray.getJSONObject(k); + String alertContent = alarmInfo.getString("alertContent"); + alarmInfo.put("alertContent",alertContent.replaceAll("\\{Value\\}",pureValue)); + parsedAlarmInfoListArray.set(k,alarmInfo); + } + } + + System.out.println("parsedAlarmInfoListArray:{}" + parsedAlarmInfoListArray); + } + + private static DynamodbEntity createBaseEntity(String content, String deviceId, String srcType, +// DeviceEntity currentDevice, +// BiFunction udfInfoFunction, + List currentDeviceAlertInfoList, + List alertTemplateIdList + ) { + DynamodbEntity baseTransDataEntity = new DynamodbEntity(); + baseTransDataEntity.setDeviceId(deviceId); + baseTransDataEntity.setSrcType(srcType); + baseTransDataEntity.setRawData(content); + baseTransDataEntity.setStatus("normal"); + long snowflakeValue=-1L; + baseTransDataEntity.setMessageId(String.valueOf(snowflakeValue)); +// baseTransDataEntity.setBuildingInfo(udfInfoFunction.apply("basic_building", currentDevice.getBuildingId()).getData()); +// baseTransDataEntity.setFloorInfo(udfInfoFunction.apply("basic_floor", currentDevice.getFloorId()).getData()); +// baseTransDataEntity.setSpaceInfo(udfInfoFunction.apply("basic_space", currentDevice.getSpaceId()).getData()); +// baseTransDataEntity.setProjectInfo(udfInfoFunction.apply("basic_project", currentDevice.getProjectId()).getData()); +// baseTransDataEntity.setEquipmentInfo(udfInfoFunction.apply("basic_monitoring_asset", currentDevice.getAssetId()).getData()); + baseTransDataEntity.setPlatformIdentifyId(UUID.randomUUID().toString()); + baseTransDataEntity.setReceive_ts(System.currentTimeMillis()); + + + + Map complexMap = DateUtils.getComplexDate(); + baseTransDataEntity.setTs(String.valueOf(System.currentTimeMillis())); + baseTransDataEntity.setYearKey(complexMap.get("yearKey")); + baseTransDataEntity.setMonthKey(complexMap.get("monthKey")); + baseTransDataEntity.setDayKey(complexMap.get("dayKey")); + baseTransDataEntity.setDateKey(complexMap.get("dateKey")); + + + if (CollectionUtil.isNotEmpty(currentDeviceAlertInfoList)) { + baseTransDataEntity.setTargetId(currentDeviceAlertInfoList.get(0).getTargetId()); + baseTransDataEntity.setProblemReportCategoryId(currentDeviceAlertInfoList.get(0).getProblemReportCategoryId()); + baseTransDataEntity.setForwardType(currentDeviceAlertInfoList.get(0).getForwardType()); + List forwardTypeList = currentDeviceAlertInfoList.stream().map(DeviceAlertInfo::getForwardType).collect(Collectors.toList()); + baseTransDataEntity.setForwardTypeList(forwardTypeList); + baseTransDataEntity.setBuildingId(currentDeviceAlertInfoList.get(0).getBuildingId()); + baseTransDataEntity.setBuildingCode(currentDeviceAlertInfoList.get(0).getBuildingCode()); + } else { + baseTransDataEntity.setTargetId(""); + baseTransDataEntity.setProblemReportCategoryId(""); + baseTransDataEntity.setForwardType("roid1"); + baseTransDataEntity.setForwardTypeList(List.of("roid1")); + baseTransDataEntity.setBuildingId(""); + baseTransDataEntity.setBuildingCode(""); + } + if (CollectionUtil.isEmpty(alertTemplateIdList)){ + baseTransDataEntity.setAlertTemplateIds(""); + }else { + baseTransDataEntity.setAlertTemplateIds(JSON.toJSONString(alertTemplateIdList)); + } + + return baseTransDataEntity; + } + + public static void main(String[] args) { + List result = new ArrayList<>(); + + + + List deviceAlertInfoList=new ArrayList<>(); +// DeviceAlertInfo deviceAlertInfo=new DeviceAlertInfo(); +// deviceAlertInfo.setForwardType("ROID1"); +// +// deviceAlertInfoList.add(deviceAlertInfo); +// DeviceAlertInfo deviceAlertInfo2=new DeviceAlertInfo(); +// deviceAlertInfo2.setForwardType("ROID2"); + + // deviceAlertInfoList.add(deviceAlertInfo2); + DynamodbEntity baseEntity = createBaseEntity("", "", "", deviceAlertInfoList, List.of(11L)); + System.out.println(JSON.toJSON(baseEntity)); + String rawData=JSON.toJSON(baseEntity).toString(); + + JSONObject currentRawJsonOBject = JSON.parseObject(rawData); + System.out.println(currentRawJsonOBject); + System.out.println(currentRawJsonOBject.getJSONArray("forwardTypeList").stream().collect(Collectors.toList())); + + JSONArray forwardTypeList = currentRawJsonOBject.getJSONArray("forwardTypeList"); + forwardTypeList.stream().map(Object::toString).collect(Collectors.toList()); + } +} diff --git a/src/test/java/com/techsor/datacenter/sender/TechsorDataCenterSenderApplicationTests.java b/src/test/java/com/techsor/datacenter/sender/TechsorDataCenterSenderApplicationTests.java new file mode 100644 index 0000000..5d8dd40 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/TechsorDataCenterSenderApplicationTests.java @@ -0,0 +1,111 @@ +package com.techsor.datacenter.sender; + +import com.baidu.fsg.uid.UidGenerator; +import com.techsor.datacenter.sender.dao.TargetForwardConfigDao; +import com.techsor.datacenter.sender.utils.RedisUtils; +import com.techsor.datacenter.sender.vo.TargetForwardConfigVO; +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import jakarta.annotation.Resource; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +import static com.techsor.datacenter.sender.service.KingIOServerService.parseDate; + +class TechsorDataCenterSenderApplicationTests { + + @Autowired + RedisUtils redisUtils; + + @Resource + TargetForwardConfigDao targetForwardConfigDao; + + @Resource + private UidGenerator uidGenerator; + + @Test + public void testUidGenerator(){ + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + String formattedTimestamp = "2025-01-28 10:45:13.917"; + String ts ="1312006394" ; + long timestampOffset = Long.parseLong(ts); + Date date = parseDate(formattedTimestamp); + date.setTime(date.getTime() + timestampOffset); + formattedTimestamp = dateFormat.format(date); + System.out.println(formattedTimestamp); + } + + @Test + void contextLoads() { + // TargetForwardConfigVO result = this.targetForwardConfigDao.selectTargetForwardConfigVOById(11L); + // Assert.assertNotNull(result); + } + // 旧电流传感器解析 485 transceiver + public void oldElectric(){ + Object a = 500; + Double calValue = (double)a *0.1; + System.out.println(calValue); + + } + + // ZETA数据例:01 2096 0000 06ae 0000 2265 0000 03be 0000 +// 01:数据收集成功 +// 2096:瞬时电力 +// 0000:瞬时电力的备用数据格 +// 06ae:積算電力量 +// 0000:積算電力量的备用数据格 +// 2265:電流 +// 0000:电流的备用数据格 +// 03be:电压 +// 0000:电压的备用数据格 +// 全部换算成10进制,然后瞬时电力×0.1w,積算電力量×0.01kwh,電流×0.001A,电压×0.1V + // 新电流传感器解析 + public void newElectric(){ + String value = "012096000006ae00002265000003be0000"; // e0000 + String deviceId = "test"; + ArrayList messageList = new ArrayList<>(); + if(value.startsWith("01") && value.length() >= 30){ + String power = value.substring(2,6); + String energy = value.substring(10,14); + String electric = value.substring(18,22); + String voltage = value.substring(26,30); + String powerValue = Float.valueOf((float)(Integer.valueOf(power,16))/10)+""; + String energyValue = Float.valueOf((float)(Integer.valueOf(energy,16))/100)+""; + String electricValue = Float.valueOf((float)(Integer.valueOf(electric,16))/1000)+""; + String voltageValue = Float.valueOf((float)(Integer.valueOf(voltage,16))/10)+""; + String message = "{\""+deviceId+"_Voltage_485\":"+voltageValue+",\""+deviceId+"_Current_485\":"+electricValue+",\""+deviceId+ + "_Instantaneous_power_485\":"+powerValue+",\""+deviceId+"_Amount_of_charge_485\":"+energyValue+"}"; + messageList.add(message); + System.out.print(message+"\n"); + System.out.printf("瞬时电力:" + powerValue + ",積算電力量:"+energyValue+",電流:"+electricValue+",电压:"+voltageValue); + } + } + + // 漏水传感器解析 + public void waterLeak(){ + String value = "00040452000004045201"; + ArrayList messageList = new ArrayList<>(); + + for (int i=0;i expressionMap = new HashMap<>(); + expressionMap.put("(getZetaDataString($this))","(getZetaDataString($this))"); + + Map> paramsMap=new HashMap<>(); + Map paramsMap1=new HashMap<>(); + paramsMap1.put("$this","$this"); + paramsMap.put("(getZetaDataString($this))",paramsMap1); + + + System.out.println(JSON.toJSONString(expressionMap)); + System.out.println(JSON.toJSONString(paramsMap)); + + + } + @Test + public void testRawData(){ + ProcessRAWEntity processRAWEntity=new ProcessRAWEntity(); + processRAWEntity.setContent("{\"a\":\"5\",\"b\":\"4\"}"); + processRAWEntity.setTs(System.currentTimeMillis()+""); + processRAWEntity.setDataSrcCode("XX-UAT"); + processRAWEntity.setId(1); + System.out.println(JSON.toJSONString(processRAWEntity)); + } + + @Resource + private IDataProcessService dataProcessService; + + @Test + public void testProcessJson(){ + + String targetJson="{\"a\":\"5\",\"b\":\"4\"}"; + this.dataProcessService.processData(targetJson,"XX-UAT"); + } + + @Test + public void testProcessJsonX(){ + + String targetJson="{\"a-a\":\"5\",\"b\":\"4\"}"; + + String result=JsonPath.parse(targetJson).read("$.a-a"); + System.out.println(result); + } + + @Test + public void testTriggerJsonA(){ + String contents="{\n" + + " \"detectorId\": \"AT000001\",\n" + + " \"AT000001_delta_accrual\": 1\n" + + "}\n"; + String alertContents="$['AT000001_delta_accrual']>100"; + Set variableSets=extractVariables(alertContents); + Map mvelMap=new HashMap<>(); + Map variableMap=new HashMap<>(); + variableSets.stream().forEach((key)->{ + if (isJsonPath(key)){ + Object tempValue = JsonPath.parse(contents).read(key); + //k做转换 + String tempPath = convertKeyVariables(key); + //存储中间变量 + mvelMap.put(tempPath, tempValue); + variableMap.put(key, tempPath); + } + }); + final String[] var = {alertContents}; + variableMap.forEach((key,value)->{ + var[0] = var[0].replace(key,value); + }); + Object result = MvelExecutor.eval(var[0], mvelMap); + String alertBool=String.valueOf(result); + System.out.println(alertBool); + System.out.println(Boolean.parseBoolean(alertBool)); + if (Boolean.valueOf(alertBool)){ + System.out.println("触发报警"); + } + } + + @Test + public void testTriggerJson(){ + String contents="{\"01_01_02_0001_A0-C\":9111}"; + String alertContents="$['01_01_02_0001_A0-C']>2000"; + Set variableSets=extractVariables(alertContents); + Map mvelMap=new HashMap<>(); + Map variableMap=new HashMap<>(); + Object resultX=JsonPath.parse(contents).read("$['01_01_02_0001_A0-C']"); + variableSets.stream().forEach((key)->{ + if (isJsonPath(key)){ + Object tempValue = JsonPath.parse(contents).read(key); + //k做转换 + String tempPath = convertKeyVariables(key); + //存储中间变量 + mvelMap.put(tempPath, tempValue); + variableMap.put(key, tempPath); + } + }); + final String[] var = {alertContents}; + variableMap.forEach((key,value)->{ + var[0] = var[0].replace(key,value); + }); + Object result = MvelExecutor.eval(var[0], mvelMap); + String alertBool=String.valueOf(result); + System.out.println(alertBool); + } + + + private String convertKeyVariables(String jsonPath) { + String result = jsonPath.replaceAll("\\$", "_var"); + String tempResult = result.replaceAll("\\.", "_"); + String tempResultA = tempResult.replaceAll("\\[", "_"); + String tempResultB = tempResultA.replaceAll("\\]", "_"); + String tempResultC = tempResultB.replaceAll("'", "_"); + String tempResultD = tempResultC.replaceAll("-", "_"); + return tempResultD; + } + private static boolean isJsonPath(String s) { + return s.startsWith("$"); + } + public static Set extractVariables(String expression) { + if (StringUtils.isEmpty(expression)){ + return new HashSet<>(); + } + Matcher matcher = JSON_PATH_PATTERN.matcher(expression); + Set variables = new HashSet<>(); + + while (matcher.find()) { + variables.add(matcher.group()); + } + + return variables; + } + // 更新的正则表达式,能够处理更复杂的嵌套模式 +// private static final Pattern JSON_PATH_PATTERN = Pattern.compile( +// "\\$\\.(?:[a-zA-Z_0-9]+(?:\\[\\d+\\]|\\['[a-zA-Z_0-9\\-_]+'\\])*(?:\\.[a-zA-Z_0-9]+(?:\\[\\d+\\]|\\['[a-zA-Z_0-9\\-_]+'\\])*)*)|\\$\\['[a-zA-Z_0-9\\-_]+'\\]" +// ); + private static final Pattern JSON_PATH_PATTERN = Pattern.compile( + "\\$\\.(?:[a-zA-Z_0-9]+(?:\\[\\d+\\]|\\['[a-zA-Z_0-9\\-_]+'\\])*(?:\\.[a-zA-Z_0-9]+(?:\\[\\d+\\]|\\['[a-zA-Z_0-9\\-_]+'\\])*)*)|\\$\\['[a-zA-Z_0-9\\-_]+'\\]" + ); + +} diff --git a/src/test/java/com/techsor/datacenter/sender/testCases/DeltaTest.java b/src/test/java/com/techsor/datacenter/sender/testCases/DeltaTest.java new file mode 100644 index 0000000..e6db401 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/testCases/DeltaTest.java @@ -0,0 +1,26 @@ +package com.techsor.datacenter.sender.testCases; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.techsor.datacenter.sender.entitiy.delta.DeltaNumEntity; +import org.junit.Test; + +import java.lang.reflect.Type; +import java.util.List; + +public class DeltaTest { + + @Test + public void testMvel(){ + String content = "{id=TTN-HMI-4-1-1A-AI, type=Boolean, reportedAt=2023-11-22-23:06:46, value=false}"; + + StringBuffer stringBuffer=new StringBuffer(); + stringBuffer.append("["); + stringBuffer.append(content); + stringBuffer.append("]"); + + Type listType = new TypeToken>() {}.getType(); + List dataList = new Gson().fromJson(stringBuffer.toString(),listType); + System.out.println("end"); + } +} diff --git a/src/test/java/com/techsor/datacenter/sender/testCases/IOServerTest.java b/src/test/java/com/techsor/datacenter/sender/testCases/IOServerTest.java new file mode 100644 index 0000000..3cffbc6 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/testCases/IOServerTest.java @@ -0,0 +1,27 @@ +package com.techsor.datacenter.sender.testCases; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.techsor.datacenter.sender.entitiy.delta.DeltaNumEntity; +import org.junit.Test; + +import java.lang.reflect.Type; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +public class IOServerTest { + + @Test + public void testMvel() throws ParseException { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + String timestampOffsetStr = "2406107480"; + long timestampOffset = Long.parseLong(timestampOffsetStr); + String tempKey = "2024-12-20 20:53:05.688"; + Date date = dateFormat.parse(tempKey); + date.setTime(date.getTime() + timestampOffset); + String formattedTimestamp = dateFormat.format(date); + System.out.println(formattedTimestamp); + } +} diff --git a/src/test/java/com/techsor/datacenter/sender/testCases/WaterLeakTest.java b/src/test/java/com/techsor/datacenter/sender/testCases/WaterLeakTest.java new file mode 100644 index 0000000..8398549 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/testCases/WaterLeakTest.java @@ -0,0 +1,32 @@ +package com.techsor.datacenter.sender.testCases; + +import com.techsor.datacenter.sender.compiler.MvelExecutor; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class WaterLeakTest { + + @Test + public void testMvel(){ + //水浸传感器-东建 + String value = "0101"; + String leakValue2 = ""; + if(value.startsWith("01")){ + if (value.endsWith("01")){ + leakValue2 = "1"; + }else if (value.endsWith("02")){ + leakValue2 = "0"; + }else if (value.equals("0100")){ + leakValue2 = "0"; + } + }else if(value.startsWith("02")){ + leakValue2 = "1"; + }else if(value.startsWith("03")){ + leakValue2 = "0"; + } + + System.out.println(leakValue2+""); + } +} diff --git a/tkbuild-demo-1739347357603-ssliotcore-mqtts-stgttkdatatechbuildcom8883/.lck b/tkbuild-demo-1739347357603-ssliotcore-mqtts-stgttkdatatechbuildcom8883/.lck new file mode 100644 index 0000000..e69de29