Compare commits

..

3 Commits

Author SHA1 Message Date
陈益
1c86475c9c MySQL.Pomelo 2025-12-07 20:12:02 +08:00
陈益
1a4777f86f 升级为9.3.4 2025-12-07 13:21:07 +08:00
陈益
8fb245d8c6 忽略 2025-12-07 11:10:08 +08:00
90 changed files with 4549 additions and 26924 deletions

71
.gitignore vendored
View File

@ -3,61 +3,60 @@
################################################################################
/.vs
/gateways/JiShe.IoT.WebGateway/bin/Debug/net10.0
/gateways/JiShe.IoT.WebGateway/bin/Debug/net9.0
/gateways/JiShe.IoT.WebGateway/obj
/host/JiShe.IoT.HttpApi.Host/bin/Debug/net10.0
/host/JiShe.IoT.HttpApi.Host/bin/Debug/net9.0
/host/JiShe.IoT.HttpApi.Host/Logs
/host/JiShe.IoT.HttpApi.Host/obj
/src/JiShe.IoT.Application/bin/Debug/net10.0
/src/JiShe.IoT.Application/obj/Debug/net10.0
/src/JiShe.IoT.Application.Contracts/bin/Debug/net10.0
/src/JiShe.IoT.Application.Contracts/obj/Debug/net10.0
/src/JiShe.IoT.DbMigrator/bin/Debug/net10.0
/src/JiShe.IoT.Application/bin/Debug/net9.0
/src/JiShe.IoT.Application/obj/Debug/net9.0
/src/JiShe.IoT.Application.Contracts/bin/Debug/net9.0
/src/JiShe.IoT.Application.Contracts/obj/Debug/net9.0
/src/JiShe.IoT.DbMigrator/bin/Debug/net9.0
/src/JiShe.IoT.DbMigrator/obj
/src/JiShe.IoT.Domain/bin/Debug/net10.0
/src/JiShe.IoT.Domain/bin/Debug/net9.0
/src/JiShe.IoT.Domain/obj
/src/JiShe.IoT.Domain.Shared/bin/Debug/net10.0
/src/JiShe.IoT.Domain.Shared/obj/Debug/net10.0
/src/JiShe.IoT.EntityFrameworkCore/bin/Debug/net10.0
/src/JiShe.IoT.Domain.Shared/bin/Debug/net9.0
/src/JiShe.IoT.Domain.Shared/obj/Debug/net9.0
/src/JiShe.IoT.EntityFrameworkCore/bin/Debug/net9.0
/src/JiShe.IoT.EntityFrameworkCore/obj
/src/JiShe.IoT.FreeSqlRepository/bin/Debug/net10.0
/src/JiShe.IoT.FreeSqlRepository/bin/Debug/net9.0
/src/JiShe.IoT.FreeSqlRepository/obj
/src/JiShe.IoT.HttpApi/bin/Debug/net10.0
/src/JiShe.IoT.HttpApi/bin/Debug/net9.0
/src/JiShe.IoT.HttpApi/obj
/src/JiShe.IoT.HttpApi.Client/bin/Debug/net10.0
/src/JiShe.IoT.HttpApi.Client/bin/Debug/net9.0
/src/JiShe.IoT.HttpApi.Client/obj
/test/JiShe.IoT.Application.Tests/bin/Debug/net10.0
/test/JiShe.IoT.Application.Tests/bin/Debug/net9.0
/test/JiShe.IoT.Application.Tests/obj
/test/JiShe.IoT.Domain.Tests/bin/Debug/net10.0
/test/JiShe.IoT.Domain.Tests/bin/Debug/net9.0
/test/JiShe.IoT.Domain.Tests/obj
/test/JiShe.IoT.EntityFrameworkCore.Tests/bin/Debug/net10.0
/test/JiShe.IoT.EntityFrameworkCore.Tests/bin/Debug/net9.0
/test/JiShe.IoT.EntityFrameworkCore.Tests/obj
/test/JiShe.IoT.HttpApi.Client.ConsoleTestApp/bin/Debug/net10.0
/test/JiShe.IoT.HttpApi.Client.ConsoleTestApp/bin/Debug/net9.0
/test/JiShe.IoT.HttpApi.Client.ConsoleTestApp/obj
/test/JiShe.IoT.TestBase/bin/Debug/net10.0
/test/JiShe.IoT.TestBase/bin/Debug/net9.0
/test/JiShe.IoT.TestBase/obj
/src/JiShe.IoT.Application/obj
/src/JiShe.IoT.Application.Contracts/obj
/src/JiShe.IoT.Domain.Shared/obj
/src/JiShe.IoT.Application/bin/Release/net10.0
/src/JiShe.IoT.Application.Contracts/bin/Release/net10.0
/src/JiShe.IoT.Domain/bin/Release/net10.0
/src/JiShe.IoT.Domain.Shared/bin/Release/net10.0
/src/JiShe.IoT.EntityFrameworkCore/bin/Release/net10.0
/src/JiShe.IoT.HttpApi/bin/Release/net10.0
/gateways/JiShe.IoT.WebGateway/bin/Release/net10.0
/host/JiShe.IoT.HttpApi.Host/bin/Release/net10.0
/host/JiShe.IoT.HttpApi.Host/bin/Release/net10.0/ca/Hangfire.Core.resources.dll
/src/JiShe.IoT.DbMigrator/bin/Release/net10.0
/src/JiShe.IoT.HttpApi.Client/bin/Release/net10.0
/test/JiShe.IoT.Application.Tests/bin/Release/net10.0
/test/JiShe.IoT.Domain.Tests/bin/Release/net10.0
/test/JiShe.IoT.EntityFrameworkCore.Tests/bin/Release/net10.0
/test/JiShe.IoT.HttpApi.Client.ConsoleTestApp/bin/Release/net10.0
/test/JiShe.IoT.TestBase/bin/Release/net10.0
/src/JiShe.IoT.Application/bin/Release/net9.0
/src/JiShe.IoT.Application.Contracts/bin/Release/net9.0
/src/JiShe.IoT.Domain/bin/Release/net9.0
/src/JiShe.IoT.Domain.Shared/bin/Release/net9.0
/src/JiShe.IoT.EntityFrameworkCore/bin/Release/net9.0
/src/JiShe.IoT.HttpApi/bin/Release/net9.0
/gateways/JiShe.IoT.WebGateway/bin/Release/net9.0
/host/JiShe.IoT.HttpApi.Host/bin/Release/net9.0
/host/JiShe.IoT.HttpApi.Host/bin/Release/net9.0/ca/Hangfire.Core.resources.dll
/src/JiShe.IoT.DbMigrator/bin/Release/net9.0
/src/JiShe.IoT.HttpApi.Client/bin/Release/net9.0
/test/JiShe.IoT.Application.Tests/bin/Release/net9.0
/test/JiShe.IoT.Domain.Tests/bin/Release/net9.0
/test/JiShe.IoT.EntityFrameworkCore.Tests/bin/Release/net9.0
/test/JiShe.IoT.HttpApi.Client.ConsoleTestApp/bin/Release/net9.0
/test/JiShe.IoT.TestBase/bin/Release/net9.0
/host/JiShe.IoT.HttpApi.Host/UploadFile/20250729/host/abp-file-management
/host/JiShe.IoT.HttpApi.Host/UploadFile/20250731/host/abp-file-management/3a1b7059-f930-8a33-5a5b-a67f48f2feec
/gateways/JiShe.IoT.WebGateway/bin/Debug/net10.0
/host/JiShe.IoT.HttpApi.Host/bin/Debug/net10.0
/src/JiShe.IoT.Application/bin/Debug/net10.0
/host/JiShe.IoT.HttpApi.Host/UploadFile

View File

@ -1,12 +1,12 @@
# FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
FROM registry.cn-qingdao.aliyuncs.com/jisheyun/aspnetcore:10.0.02_basic_ub24_dmpv35 AS base
FROM registry.cn-qingdao.aliyuncs.com/jisheyun/aspnetcore:9.0.6_ub24_simsun_pdp_v341 AS base
WORKDIR /app
EXPOSE 10500
EXPOSE 443
ENV TZ=Asia/Shanghai
ENV ASPNETCORE_ENVIRONMENT=Development
FROM mcr.microsoft.com/dotnet/sdk:10.0.102 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
COPY ["JiShe.IoT.Admin.sln", "."]
COPY ["Directory.Build.JiShe.targets", "."]
COPY ["Directory.Build.Microsoft.targets", "."]

View File

@ -20,11 +20,8 @@
<PackageReference Update="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0"/>
<PackageReference Update="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0"/>
<PackageReference Update="Microsoft.AspNetCore.Http.Features" Version="2.3.0"/>
<PackageReference Update="Microsoft.CSharp" Version="5.0.0"/>
<PackageReference Update="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageReference Update="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
<PackageReference Update="Microsoft.CodeAnalysis.Common " Version="5.0.0" />
<PackageReference Update="Microsoft.CodeAnalysis.Workspaces.Common" Version="5.0.0" />
<PackageReference Update="Microsoft.CSharp" Version="4.12.0"/>
<PackageReference Update="Microsoft.CodeAnalysis.CSharp" Version="4.12.0"/>
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
</ItemGroup>
</Project>

View File

@ -4,12 +4,12 @@
<!--JiShe.ServicePro版本-->
<ServiceProVersion>1.0.5.40</ServiceProVersion>
<!--Volo Abp 版本-->
<VoloAbpVersion>10.0.2</VoloAbpVersion>
<VoloAbpVersion>9.3.4</VoloAbpVersion>
<!--Dotnet 组件 版本-->
<DotnetInfoVersion>10.0.2</DotnetInfoVersion>
<DotnetInfoVersion>9.0.6</DotnetInfoVersion>
<!--Dotnet 框架 版本-->
<DotnetTargetFrameworkVersion>net10.0</DotnetTargetFrameworkVersion>
<DotnetTargetFrameworkVersion>net9.0</DotnetTargetFrameworkVersion>
</PropertyGroup>
</Project>

View File

@ -28,18 +28,6 @@
<PackageReference Update="Ocelot.Provider.Consul" Version="18.0.0"/>
<PackageReference Update="Ocelot.Provider.Polly" Version="18.0.0"/>
<!--FreeRedis-->
<PackageReference Update="FreeRedis" Version="1.3.7"/>
<PackageReference Update="FreeRedis.DistributedCache" Version="1.3.7"/>
<!--FreeSql-->
<PackageReference Update="FreeSql.Cloud" Version="2.0.1" />
<PackageReference Update="FreeSql.Extensions.JsonMap" Version="3.5.305" />
<PackageReference Update="FreeSql" Version="3.5.305"/>
<PackageReference Update="FreeSql.Provider.MysqlConnector" Version="3.5.305"/>
<PackageReference Update="FreeSql.Provider.Sqlite" Version="3.5.305"/>
<PackageReference Update="FreeSql.Provider.SqlServer" Version="3.5.305" />
<PackageReference Update="FreeSql.Provider.PostgreSQL " Version="3.5.305" />
<!-- 单元测试包-->
<PackageReference Update="xunit" Version="2.9.3"/>
@ -63,9 +51,9 @@
<PackageReference Update="Savorboard.CAP.InMemoryMessageQueue" Version="8.2.1"/>
<!-- Swagger-->
<PackageReference Update="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.4"/>
<PackageReference Update="Swashbuckle.AspNetCore" Version="9.0.4"/>
<PackageReference Update="Swashbuckle.AspNetCore.Annotations" Version="9.0.4"/>
<PackageReference Update="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.0"/>
<PackageReference Update="Swashbuckle.AspNetCore" Version="9.0.0"/>
<PackageReference Update="Swashbuckle.AspNetCore.Annotations" Version="9.0.0"/>
<!-- Serilog 日志-->
<PackageReference Update="Serilog" Version="4.3.0"/>
@ -81,8 +69,8 @@
<!-- Magicodes 导入导出Excel-->
<PackageReference Update="Magicodes.IE.Excel" Version="2.8.0"/>
<PackageReference Update="Magicodes.IE.Excel.AspNetCore" Version="2.8.0"/>
<PackageReference Update="Magicodes.IE.Excel" Version="2.7.5.2"/>
<PackageReference Update="Magicodes.IE.Excel.AspNetCore" Version="2.7.5.2"/>
<!-- MiniProfiler 查看接口耗时,sql耗时-->
<PackageReference Update="MiniProfiler.AspNetCore.Mvc" Version="4.5.4"/>
@ -116,29 +104,9 @@
<PackageReference Update="Otp.NET" Version="1.4.0" />
<PackageReference Update="QRCoder" Version="1.6.0" />
<!--Flurl-->
<PackageReference Update="Flurl.Http" Version="4.0.2" />
<PackageReference Update="Mapster" Version="7.4.0" />
<!--Apache.IoTDB-->
<PackageReference Update="Apache.IoTDB" Version="2.0.2.15" />
<!--Kafka-->
<PackageReference Update="Confluent.Kafka" Version="2.11.0" />
<!--DotPulsar-->
<PackageReference Update="DotPulsar" Version="5.1.0" />
<PackageReference Update="K4os.Compression.LZ4" Version="1.3.8" />
<PackageReference Update="ZstdNet" Version="1.4.5" />
<PackageReference Update="ZstdSharp.Port" Version="0.8.6" />
<!--SixLabors.ImageSharp-->
<PackageReference Update="SixLabors.ImageSharp" Version="3.1.12" />
<!--SkiaSharp-->
<PackageReference Update="SkiaSharp" Version="3.119.1" />
</ItemGroup>
</Project>

View File

@ -1,29 +1,34 @@
# 服务器配置
# 修改为你的服务器IP地址
SERVER_IP=47.110.53.196
SERVER_IP=192.168.111.174
# Pulsar配置
PULSAR_BROKER_PORT=9093
PULSAR_ADMIN_PORT=9094
PULSAR_ZOOKEEPER_PORT=2181
PULSAR_BROKER_EXTERNAL_PORT=26974
PULSAR_ADMIN_USER=admin
PULSAR_ADMIN_PASSWORD=0fd7afb8b0d04e6abc4fdfdac2190a79
# IoTDB配置
IOTDB_PORT=30710
IOTDB_ROOT_PASSWORD=Li#xiao@198!0
# Redis配置
REDIS_PORT=30712
# PostgreSQL配置
POSTGRES_PORT=35432
POSTGRES_DB=jisheiotprodb
POSTGRES_PASSWORD=JiShe!aqG#5kGgh&0
# MySQL配置
MYSQL_PORT=13306
# Admin API配置
ADMIN_API_PORT=28080
# Admin Web配置
ADMIN_WEB_PORT=30711
#MSSQL Server配置
SQLSERVER_EDITION=Developer
SQLSERVER_PORT=11433
# 认证信息
PULSAR_ADMIN_USER=admin
PULSAR_ADMIN_PASSWORD=0fd7afb8b0d04e6abc4fdfdac2190a79
IOTDB_ROOT_PASSWORD=Lixiao@1980
MYSQL_ROOT_PASSWORD='JiShe!aqG#5kGgh&0'
SQLSERVER_SA_PASSWORD=JiShe!aqG@5kGgh&0

View File

@ -1 +0,0 @@
jisheiotdata

View File

@ -4,7 +4,7 @@
"CorsOrigins": "https://*.IoT.com,http://localhost:4200,http://localhost:3100,http://localhost:80,http://10.10.70.11:4200,http://47.110.53.196:30711,http://192.168.111.174:30711"
},
"ConnectionStrings": {
"Default": "Host=postgres;Port=5432;Database=jisheiotprodb;Username=postgres;Password=JiShe!aqG#5kGgh&0;Encoding=UTF8;Pooling=true;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=100;"
"Default": "Data Source=mysql;Port=3306;Database=JiSheIoTProDB;uid=root;pwd=JiShe!aqG#5kGgh&0;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;"
},
"Hangfire": {
"Redis": {
@ -44,7 +44,7 @@
"DefaultPartitions": 16,
"DefaultBundles": 16,
"EnableAutoCreation": true, //Topic
"TopicMode": "Static", //DynamicStatic
"TopicMode": "Static", //Dynamic
"EnableTopicTypeFilter": true, //Topic
"AllowedTopicTypes": [ "Static" ], //Topic
"AllowedClusters": [ "pulsar-cluster-1" ], //
@ -56,7 +56,7 @@
},
"IoTDBOptions": {
"UserName": "root",
"Password": "Li#xiao@198!0",
"Password": "Lixiao@1980",
"TreeModelClusterList": [ "iotdb-standalone:6667" ],
"TableModelClusterList": [ "iotdb-standalone:6667" ],
"PoolSize": 32,

View File

@ -4,7 +4,7 @@
"CorsOrigins": "https://*.IoT.com,http://localhost:4200,http://localhost:3100,http://localhost:80,http://10.10.70.11:4200,http://47.110.53.196:30711,http://192.168.111.174:30711"
},
"ConnectionStrings": {
"Default": "Host=postgres;Port=5432;Database=jisheiotprodb;Username=postgres;Password=JiShe!aqG#5kGgh&0;Encoding=UTF8;Pooling=true;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=100;"
"Default": "Data Source=mysql;Port=3306;Database=JiSheIoTProDB;uid=root;pwd=JiShe!aqG#5kGgh&0;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;"
},
"Hangfire": {
"Redis": {
@ -44,7 +44,7 @@
"DefaultPartitions": 16,
"DefaultBundles": 16,
"EnableAutoCreation": false, //Topic
"TopicMode": "Dynamic", //DynamicStatic
"TopicMode": "Dynamic", //Dynamic
"EnableTopicTypeFilter": true, //Topic
"AllowedTopicTypes": [ "Dynamic" ], //Topic
"AllowedClusters": [ "pulsar-cluster-1" ], //
@ -56,7 +56,7 @@
},
"IoTDBOptions": {
"UserName": "root",
"Password": "Li#xiao@198!0",
"Password": "Lixiao@1980",
"TreeModelClusterList": [ "iotdb-standalone:6667" ],
"TableModelClusterList": [ "iotdb-standalone:6667" ],
"PoolSize": 32,

View File

@ -183,7 +183,7 @@ services:
- pulsar-net
iotdb-standalone-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.6-standalone
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.5-standalone
hostname: iotdb-standalone
container_name: iotdb-standalone
restart: always
@ -236,41 +236,39 @@ services:
networks:
- pulsar-net
postgres-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/postgres:18.1-alpine
container_name: postgres
hostname: postgres
restart: unless-stopped
mysql-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/mysql:8.4.6
container_name: mysql
hostname: mysql
restart: always
privileged: true
user: "1000:1000" # 使用宿主机用户ID
ports:
- "${POSTGRES_PORT:-5432}:5432"
- "${MYSQL_PORT}:3306"
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-JiShe!aqG#5kGgh&0}
- POSTGRES_DB=${POSTGRES_DB:-jisheiotprodb}
- POSTGRES_INITDB_ARGS=--encoding=UTF8 --lc-collate=C --lc-ctype=C
- TZ=Asia/Shanghai
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
TZ: Asia/Shanghai
volumes:
- ./postgresql/data:/var/lib/postgresql
- ./postgresql/init:/docker-entrypoint-initdb.d
- ./mysql/log:/var/log/mysql
- ./mysql/data:/var/lib/mysql
- ./mysql/init:/docker-entrypoint-initdb.d
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --user=mysql # 确保MySQL以mysql用户运行
- --binlog-expire-logs-seconds=604800 # 设置binlog保留7天7*24*60*60=604800秒
deploy:
resources:
limits:
cpus: "2.0"
memory: 2g
stdin_open: true
tty: true
networks:
- pulsar-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
admin-api-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.admin:2026.0203.0900
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.admin:2025.1119.1155
container_name: admin-api
hostname: admin-api
restart: always
@ -287,8 +285,8 @@ services:
stdin_open: true
tty: true
depends_on:
postgres-service:
condition: service_healthy
mysql-service:
condition: service_started
redis-service:
condition: service_started
iotdb-standalone-service:
@ -301,7 +299,7 @@ services:
- pulsar-net
admin-web-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.ui:2026.0203.0900
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.ui:2025.1030.0949
container_name: admin-web
hostname: admin-web
restart: always
@ -326,8 +324,8 @@ networks:
ipam:
driver: default
config:
- subnet: 172.24.0.0/16
gateway: 172.24.0.1
- subnet: 172.23.0.0/16
gateway: 172.23.0.1
volumes:
pulsar-zookeeper-data:

View File

@ -1,6 +1,6 @@
services:
iotdb-standalone:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.6-standalone
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.5-standalone
hostname: iotdb-standalone
container_name: iotdb-standalone
restart: "no"

View File

@ -1,18 +0,0 @@
postgres:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/postgres:18.1-alpine
container_name: postgres
environment:
- POSTGRES_PASSWORD=JiShe!aqG#5kGgh&0
- POSTGRES_DB=jisheiotprodb
- POSTGRES_INITDB_ARGS=--encoding=UTF8 --lc-collate=C --lc-ctype=C
volumes:
- /mnt/dockerdata/postgresql/data:/var/lib/postgresql
ports:
- "5432:5432"
restart: unless-stopped
healthcheck:
# 修改为默认的 postgres 用户
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

View File

@ -27,11 +27,6 @@ services:
timeout: 10s
retries: 5
start_period: 60s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
bookie:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/pulsar:4.1.1
@ -70,11 +65,6 @@ services:
timeout: 10s
retries: 5
start_period: 60s
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "3"
broker:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/pulsar:4.1.1
@ -145,14 +135,9 @@ services:
timeout: 10s
retries: 5
start_period: 60s
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "3"
iotdb-standalone-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.6-standalone
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.5-standalone
hostname: iotdb-standalone
container_name: iotdb-standalone
restart: always
@ -183,11 +168,6 @@ services:
- ./iotdb/logs:/iotdb/logs
networks:
- pulsar-net
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "5"
redis-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/redis:8.0.2-alpine3.21
@ -209,46 +189,38 @@ services:
tty: true
networks:
- pulsar-net
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
postgres-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/postgres:18.1-alpine
container_name: postgres
hostname: postgres
restart: unless-stopped
mysql-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/mysql:8.4.6
container_name: mysql
hostname: mysql
restart: always
privileged: true
user: "1000:1000" # 使用宿主机用户ID
ports:
- "${POSTGRES_PORT:-5432}:5432"
- "${MYSQL_PORT}:3306"
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-JiShe!aqG#5kGgh&0}
- POSTGRES_DB=${POSTGRES_DB:-jisheiotprodb}
- POSTGRES_INITDB_ARGS=--encoding=UTF8 --lc-collate=C --lc-ctype=C
- TZ=Asia/Shanghai
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
TZ: Asia/Shanghai
volumes:
- ./postgresql/data:/var/lib/postgresql
- ./mysql/log:/var/log/mysql
- ./mysql/data:/var/lib/mysql
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --binlog-expire-logs-seconds=604800 # 设置binlog保留7天7*24*60*60=604800秒
deploy:
resources:
limits:
cpus: "2.0"
memory: 2g
stdin_open: true
tty: true
networks:
- pulsar-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
admin-api-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.admin:2026.0203.0900
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.admin:2025.1119.1155
container_name: admin-api
hostname: admin-api
restart: always
@ -264,8 +236,8 @@ services:
stdin_open: true
tty: true
depends_on:
postgres-service:
condition: service_healthy
mysql-service:
condition: service_started
redis-service:
condition: service_started
iotdb-standalone-service:
@ -276,14 +248,9 @@ services:
condition: service_healthy
networks:
- pulsar-net
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
admin-web-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.ui:2026.0203.0900
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.ui:2025.1030.0949
container_name: admin-web
hostname: admin-web
restart: always
@ -301,11 +268,6 @@ services:
condition: service_started
networks:
- pulsar-net
logging:
driver: "json-file"
options:
max-size: "20m"
max-file: "2"
networks:
pulsar-net:
@ -313,8 +275,8 @@ networks:
ipam:
driver: default
config:
- subnet: 172.24.0.0/16
gateway: 172.24.0.1
- subnet: 172.23.0.0/16
gateway: 172.23.0.1
volumes:
pulsar-zookeeper-data:

View File

@ -1,6 +1,6 @@
services:
iotdb-standalone-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.6-standalone
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.5-standalone
hostname: iotdb-standalone
container_name: iotdb-standalone
restart: always
@ -31,11 +31,6 @@ services:
- ./iotdb/logs:/iotdb/logs
networks:
- pulsar-net
logging:
driver: "json-file"
options:
max-size: "200m"
max-file: "5"
redis-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/redis:8.0.2-alpine3.21
@ -57,46 +52,38 @@ services:
tty: true
networks:
- pulsar-net
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
postgres-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/postgres:18.1-alpine
container_name: postgres
hostname: postgres
restart: unless-stopped
mysql-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/mysql:8.4.6
container_name: mysql
hostname: mysql
restart: always
privileged: true
user: "1000:1000" # 使用宿主机用户ID
ports:
- "${POSTGRES_PORT:-5432}:5432"
- "${MYSQL_PORT}:3306"
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-JiShe!aqG#5kGgh&0}
- POSTGRES_DB=${POSTGRES_DB:-jisheiotprodb}
- POSTGRES_INITDB_ARGS=--encoding=UTF8 --lc-collate=C --lc-ctype=C
- TZ=Asia/Shanghai
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
TZ: Asia/Shanghai
volumes:
- ./postgresql/data:/var/lib/postgresql/data
- ./mysql/log:/var/log/mysql
- ./mysql/data:/var/lib/mysql
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --binlog-expire-logs-seconds=604800 # 设置binlog保留7天7*24*60*60=604800秒
deploy:
resources:
limits:
cpus: "2.0"
memory: 2g
stdin_open: true
tty: true
networks:
- pulsar-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
admin-api-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.admin:2026.0203.0900
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.admin:2025.1119.1155
container_name: admin-api
hostname: admin-api
restart: always
@ -112,22 +99,17 @@ services:
stdin_open: true
tty: true
depends_on:
postgres-service:
condition: service_healthy
mysql-service:
condition: service_started
redis-service:
condition: service_started
iotdb-standalone-service:
condition: service_started
networks:
- pulsar-net
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
admin-web-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.ui:2026.0203.0900
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.ui:2025.1030.0949
container_name: admin-web
hostname: admin-web
restart: always
@ -145,11 +127,6 @@ services:
condition: service_started
networks:
- pulsar-net
logging:
driver: "json-file"
options:
max-size: "20m"
max-file: "2"
networks:
pulsar-net:

View File

@ -1,6 +1,6 @@
services:
iotdb-standalone-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.6-standalone
image: registry.cn-qingdao.aliyuncs.com/jisheyun/iotdb:2.0.5-standalone
hostname: iotdb-standalone
container_name: iotdb-standalone
restart: always
@ -53,41 +53,39 @@ services:
networks:
- pulsar-net
postgres-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/postgres:18.1-alpine
container_name: postgres
hostname: postgres
restart: unless-stopped
mysql-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/mysql:8.4.6
container_name: mysql
hostname: mysql
restart: always
privileged: true
user: "1000:1000" # 使用宿主机用户ID
ports:
- "${POSTGRES_PORT:-5432}:5432"
- "${MYSQL_PORT}:3306"
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-JiShe!aqG#5kGgh&0}
- POSTGRES_DB=${POSTGRES_DB:-jisheiotprodb}
- POSTGRES_INITDB_ARGS=--encoding=UTF8 --lc-collate=C --lc-ctype=C
- TZ=Asia/Shanghai
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
TZ: Asia/Shanghai
volumes:
- ./postgresql/data:/var/lib/postgresql/data
- ./postgresql/init:/docker-entrypoint-initdb.d
- ./mysql/log:/var/log/mysql
- ./mysql/data:/var/lib/mysql
- ./mysql/init:/docker-entrypoint-initdb.d
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --user=mysql # 确保MySQL以mysql用户运行
- --binlog-expire-logs-seconds=604800 # 设置binlog保留7天7*24*60*60=604800秒
deploy:
resources:
limits:
cpus: "2.0"
memory: 2g
stdin_open: true
tty: true
networks:
- pulsar-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
admin-api-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.admin:2026.0203.0900
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.admin:2025.1119.1155
container_name: admin-api
hostname: admin-api
restart: always
@ -104,8 +102,8 @@ services:
stdin_open: true
tty: true
depends_on:
postgres-service:
condition: service_healthy
mysql-service:
condition: service_started
redis-service:
condition: service_started
iotdb-standalone-service:
@ -114,7 +112,7 @@ services:
- pulsar-net
admin-web-service:
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.ui:2026.0203.0900
image: registry.cn-qingdao.aliyuncs.com/jisheyun/jishe.iot.ui:2025.1030.0949
container_name: admin-web
hostname: admin-web
restart: always

View File

@ -13,13 +13,8 @@ IOTDB_PORT=30710
# Redis配置
REDIS_PORT=30712
# PostgreSQL配置
POSTGRES_PORT=5432
POSTGRES_PASSWORD=JiShe!aqG#5kGgh&0
POSTGRES_DB=jisheiotprodb
POSTGRES_USER=postgres
POSTGRES_CONTAINER=postgres
POSTGRES_BACKUP_DIR=/mnt/dockerdata/appservice/postgresql/backup
# MySQL配置
MYSQL_PORT=13306
# Admin API配置
ADMIN_API_PORT=28080
@ -31,3 +26,4 @@ ADMIN_WEB_PORT=30711
PULSAR_ADMIN_USER=admin
PULSAR_ADMIN_PASSWORD=0fd7afb8b0d04e6abc4fdfdac2190a79
IOTDB_ROOT_PASSWORD=Lixiao@1980
MYSQL_ROOT_PASSWORD=JiShe!aqG#5kGgh&0

View File

@ -1,7 +1 @@
CREATE DATABASE jisheiotdata;USE jisheiotdata;
CREATE TABLE IF NOT EXISTS CTWingAepReceiveMessageEntity( time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, PlatformTenantId STRING FIELD, ProductId STRING FIELD, ServiceId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD, IMSI STRING FIELD, IMEI STRING FIELD ) COMMENT 'CTWingAepReceiveMessageEntity' ;
CREATE TABLE IF NOT EXISTS DeviceTelemetryPacketTaskInfo( time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, IoTPlatformProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, IoTPlatformProductName STRING FIELD, IoTPlatformAccountId STRING FIELD, AccountPhoneNumber STRING FIELD, IssueRawMessage STRING FIELD, IssuePayload STRING FIELD, ResponseRawMessage STRING FIELD, ResponsePayload STRING FIELD, TelemetrySource INT32 FIELD, IoTPlatform INT32 FIELD, RetryCount INT32 FIELD, LastIssueTime TIMESTAMP FIELD, IssueStatus INT32 FIELD ) COMMENT 'DeviceTelemetryPacketTaskInfo' ;
CREATE TABLE IF NOT EXISTS OneNETReceiveMessageEntity( time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, ProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, IsEncrypted BOOLEAN FIELD, PlaintextMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD ) COMMENT 'OneNETReceiveMessageEntity'
CREATE TABLE IF NOT EXISTS CTWingAepReceiveMessageEntity(time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, SubDevice STRING TAG, PlatformTenantId STRING FIELD, ProductId STRING FIELD, ServiceId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD, IMSI STRING FIELD, IMEI STRING FIELD ) COMMENT 'CTWingAepReceiveMessageEntity';CREATE TABLE IF NOT EXISTS DeviceTelemetryPacketTaskInfo(time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, SubDevice STRING TAG, IoTPlatformProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, IoTPlatformProductName STRING FIELD, IoTPlatformAccountId STRING FIELD, AccountPhoneNumber STRING FIELD, TelemetryType INT32 FIELD, IssueRawMessage STRING FIELD, IssuePayload STRING FIELD, ResponseRawMessage STRING FIELD, ResponsePayload STRING FIELD, TelemetrySource INT32 FIELD, IoTPlatform INT32 FIELD, RetryCount INT32 FIELD, LastIssueTime TIMESTAMP FIELD, IssueStatus INT32 FIELD ) COMMENT 'DeviceTelemetryPacketTaskInfo';CREATE TABLE IF NOT EXISTS OneNETReceiveMessageEntity(time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, SubDevice STRING TAG, ProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, IsEncrypted BOOLEAN FIELD, PlaintextMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD ) COMMENT 'OneNETReceiveMessageEntity';

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,25 +0,0 @@
-- PostgreSQL 初始化脚本:创建应用程序用户
-- 此脚本会在数据库首次初始化时自动执行
-- 创建应用程序用户 iotmanage如果不存在
DO $$
BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_user WHERE usename = 'iotmanage') THEN
CREATE USER iotmanage WITH PASSWORD 'Iage@Passo#43tMand123!';
RAISE NOTICE '用户 iotmanage 创建成功';
ELSE
RAISE NOTICE '用户 iotmanage 已存在';
END IF;
END
$$;
-- 授予数据库权限
-- 将 jisheiotprodb 数据库的所有权限授予 iotmanage 用户
GRANT ALL PRIVILEGES ON DATABASE jisheiotprodb TO iotmanage;
-- 授予创建数据库权限(如果需要)
ALTER USER iotmanage CREATEDB;
-- 注意schema 权限需要在连接到具体数据库后授予
-- 由于初始化脚本在创建数据库后执行schema 权限会在数据库创建后自动授予
-- 如果需要更细粒度的权限控制,可以创建单独的脚本文件

View File

@ -1,17 +0,0 @@
-- 授予 schema 权限给 iotmanage 用户
-- 此脚本会在数据库创建后执行,此时可以连接到具体数据库
-- 连接到 jisheiotprodb 数据库并授予权限
-- 注意:在初始化脚本中,每个 SQL 文件都会自动连接到 POSTGRES_DB 指定的数据库
-- 授予 public schema 的所有权限
GRANT ALL ON SCHEMA public TO iotmanage;
-- 设置默认权限:新创建的表和序列自动授予 iotmanage 用户
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO iotmanage;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO iotmanage;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO iotmanage;
-- 如果已有表,需要手动授予权限(在表创建后执行)
-- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO iotmanage;
-- GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO iotmanage;

View File

@ -1,58 +0,0 @@
# PostgreSQL 初始化脚本说明
## 目录说明
`./postgresql/init/` 目录下的脚本会在 PostgreSQL 容器**首次启动**时自动执行。
## 执行规则
1. **仅执行一次**:只有当数据目录为空时才会执行
2. **执行顺序**:按文件名字母顺序执行
3. **支持格式**
- `.sh` - Shell 脚本
- `.sql` - SQL 脚本
- `.sql.gz` - 压缩的 SQL 脚本
## 用户说明
### postgres 用户
- **默认超级用户**,总是存在,无法删除
- 拥有所有权限
- 用于数据库管理操作
### iotmanage 用户
- **应用程序用户**,通过初始化脚本创建
- 拥有指定数据库的权限
- 用于应用程序连接
## 文件命名建议
建议使用数字前缀来确保执行顺序:
- `01-create-user.sql` - 创建用户
- `02-create-schema.sql` - 创建 schema
- `03-create-tables.sql` - 创建表结构
- `04-insert-data.sql` - 插入初始数据
## 注意事项
1. 如果数据目录已有数据,这些脚本**不会再次执行**
2. 如果需要重新初始化,需要删除数据目录:`rm -rf ./postgresql/data/*`
3. 脚本中的密码建议使用环境变量,不要硬编码
4. 确保脚本具有幂等性(可以安全地重复执行)
## 示例:使用环境变量
```sql
-- 从环境变量读取密码(需要在脚本中处理)
CREATE USER iotmanage WITH PASSWORD :'IOTMANAGE_PASSWORD';
```
## 验证用户
```bash
# 查看所有用户
docker exec -it postgres psql -U postgres -c "\du"
# 测试 iotmanage 用户连接
docker exec -it postgres psql -U iotmanage -d jisheiotprodb -c "SELECT current_user;"
```

View File

@ -62,23 +62,25 @@ echo "✅ 认证配置完成 - 用户名: ${PULSAR_ADMIN_USER}, 密码: ${PULSAR
# 创建数据目录
echo "📁 创建数据目录..."
sudo mkdir -p /mnt/dockerdata/appservice/{pulsar,redis,postgresql,iotdb,adminapi}
sudo mkdir -p /mnt/dockerdata/appservice/{pulsar,redis,mysql,iotdb,adminapi}
sudo mkdir -p /mnt/dockerdata/appservice/pulsar/{zookeeper-data,bookkeeper-data,pulsar-data}
sudo mkdir -p /mnt/dockerdata/appservice/{redis/{outdata,conf},postgresql/data,iotdb/{conf,data,logs},adminapi/{conf,logs}}
sudo mkdir -p /mnt/dockerdata/appservice/{redis/{outdata,conf},mysql/{conf,log,data},iotdb/{conf,data,logs},adminapi/{conf,logs}}
sudo chown -R 10000:0 /mnt/dockerdata/appservice/pulsar/
# 创建本地目录的软链接(如果不存在)
echo "🔗 创建本地目录链接..."
mkdir -p ./pulsar/{zookeeper-data,bookkeeper-data,pulsar-data,auth}
mkdir -p ./redis/{outdata,conf}
mkdir -p ./postgresql/{data,init}
mkdir -p ./mysql/{conf,log,data}
mkdir -p ./iotdb/{conf,data,logs}
mkdir -p ./adminapi/{conf,logs}
# 修复PostgreSQL目录权限
echo "🔧 修复PostgreSQL目录权限..."
sudo chown -R 999:999 ./postgresql/data 2>/dev/null || true
sudo chmod -R 755 ./postgresql/data 2>/dev/null || true
# 修复MySQL目录权限
echo "🔧 修复MySQL目录权限..."
sudo chown -R 1000:1000 ./mysql/data
sudo chown -R 1000:1000 ./mysql/log
sudo chmod -R 755 ./mysql/data
sudo chmod -R 755 ./mysql/log
sudo chmod -R 755 ./adminapi/logs
@ -122,21 +124,19 @@ sudo rm -rf ./iotdb/data/*
sudo rm -rf ./iotdb/logs/*
echo "✅ IoTDB数据目录已清理配置目录已保留"
# PostgreSQL配置检查
echo "⚙️ 检查PostgreSQL配置..."
if [ ! -d "./postgresql/init" ]; then
echo "⚠️ PostgreSQL初始化脚本目录不存在将创建目录可选"
mkdir -p ./postgresql/init
# MySQL配置检查
echo "⚙️ 检查MySQL配置..."
if [ ! -d "./mysql/init" ]; then
echo "❌ MySQL初始化脚本目录不存在请检查"
exit 1
fi
# PostgreSQL初始化脚本是可选的不强制要求
if [ -f "./postgresql/init/init-db.sql" ]; then
echo "✅ PostgreSQL初始化脚本已存在"
else
echo " PostgreSQL初始化脚本不存在可选将在首次启动时使用环境变量创建数据库"
if [ ! -f "./mysql/init/init-db.sql" ]; then
echo "❌ MySQL初始化脚本不存在请检查"
exit 1
fi
echo "✅ PostgreSQL配置检查通过"
echo "✅ MySQL配置检查通过"
# 删除服务订阅模式配置文件(如果存在)
echo "🗑️ 清理服务订阅模式配置文件..."
@ -170,7 +170,7 @@ sleep 10
echo "IoTDB服务已就绪开始创建表模型数据库和设置root账号密码..."
# 使用非交互式方式创建表模型数据库
echo "CREATE DATABASE jisheiotdata;USE jisheiotdata;CREATE TABLE IF NOT EXISTS CTWingAepReceiveMessageEntity( time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, PlatformTenantId STRING FIELD, ProductId STRING FIELD, ServiceId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD, IMSI STRING FIELD, IMEI STRING FIELD ) COMMENT 'CTWingAepReceiveMessageEntity' ;CREATE TABLE IF NOT EXISTS DeviceTelemetryPacketTaskInfo( time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, IoTPlatformProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, IoTPlatformProductName STRING FIELD, IoTPlatformAccountId STRING FIELD, AccountPhoneNumber STRING FIELD, IssueRawMessage STRING FIELD, IssuePayload STRING FIELD, ResponseRawMessage STRING FIELD, ResponsePayload STRING FIELD, TelemetrySource INT32 FIELD, IoTPlatform INT32 FIELD, RetryCount INT32 FIELD, LastIssueTime TIMESTAMP FIELD, IssueStatus INT32 FIELD ) COMMENT 'DeviceTelemetryPacketTaskInfo' ;CREATE TABLE IF NOT EXISTS OneNETReceiveMessageEntity( time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, ProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, IsEncrypted BOOLEAN FIELD, PlaintextMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD ) COMMENT 'OneNETReceiveMessageEntity'" | ./start-cli.sh -h iotdb-standalone -sql_dialect table
echo "CREATE TABLE IF NOT EXISTS CTWingAepReceiveMessageEntity(time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, SubDevice STRING TAG, PlatformTenantId STRING FIELD, ProductId STRING FIELD, ServiceId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD, IMSI STRING FIELD, IMEI STRING FIELD ) COMMENT 'CTWingAepReceiveMessageEntity';CREATE TABLE IF NOT EXISTS DeviceTelemetryPacketTaskInfo(time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, SubDevice STRING TAG, IoTPlatformProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, IoTPlatformProductName STRING FIELD, IoTPlatformAccountId STRING FIELD, AccountPhoneNumber STRING FIELD, TelemetryType INT32 FIELD, IssueRawMessage STRING FIELD, IssuePayload STRING FIELD, ResponseRawMessage STRING FIELD, ResponsePayload STRING FIELD, TelemetrySource INT32 FIELD, IoTPlatform INT32 FIELD, RetryCount INT32 FIELD, LastIssueTime TIMESTAMP FIELD, IssueStatus INT32 FIELD ) COMMENT 'DeviceTelemetryPacketTaskInfo';CREATE TABLE IF NOT EXISTS OneNETReceiveMessageEntity(time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, SubDevice STRING TAG, ProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, IsEncrypted BOOLEAN FIELD, PlaintextMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD ) COMMENT 'OneNETReceiveMessageEntity';" | ./start-cli.sh -h iotdb-standalone -sql_dialect table
# 使用非交互式方式设置密码
echo "ALTER USER root SET PASSWORD '${IOTDB_ROOT_PASSWORD}';" | ./start-cli.sh -h iotdb-standalone
@ -188,7 +188,7 @@ echo " Pulsar Broker: ${SERVER_IP}:${PULSAR_BROKER_PORT}"
echo " Pulsar Admin: http://${SERVER_IP}:${PULSAR_ADMIN_PORT}"
echo " IoTDB: ${SERVER_IP}:${IOTDB_PORT} (root/${IOTDB_ROOT_PASSWORD})"
echo " Redis: ${SERVER_IP}:6379"
echo " PostgreSQL: ${SERVER_IP}:${POSTGRES_PORT:-5432} (postgres/${POSTGRES_PASSWORD:-JiShe!aqG#5kGgh&0})"
echo " MySQL: ${SERVER_IP}:${MYSQL_PORT}"
echo " Admin API: http://${SERVER_IP}:${ADMIN_API_PORT}"
echo " Admin UI: http://${SERVER_IP}:${ADMIN_WEB_PORT}"
echo " 用户名: ${PULSAR_ADMIN_USER}"
@ -202,7 +202,7 @@ echo ""
echo "🧪 测试连接:"
echo " Pulsar: curl -u ${PULSAR_ADMIN_USER}:${PULSAR_ADMIN_PASSWORD} http://${SERVER_IP}:${PULSAR_ADMIN_PORT}/admin/v2/clusters"
echo " Redis: redis-cli -h ${SERVER_IP} -p 6379 ping"
echo " PostgreSQL: PGPASSWORD=${POSTGRES_PASSWORD:-JiShe!aqG#5kGgh&0} psql -h ${SERVER_IP} -p ${POSTGRES_PORT:-5432} -U postgres -d ${POSTGRES_DB:-jisheiotprodb} -c 'SELECT 1'"
echo " MySQL: mysql -h ${SERVER_IP} -P ${MYSQL_PORT} -uroot -p${MYSQL_ROOT_PASSWORD} -e 'SELECT 1'"
echo " IoTDB: docker exec -it iotdb-standalone ./start-cli.sh -h iotdb-standalone -u root -p Lixiao@1980"
echo ""
echo "🎯 等待服务启动完成..."

View File

@ -42,20 +42,22 @@ echo "=== 采集端一键启动脚本 ==="
# 创建数据目录
echo "📁 创建数据目录..."
sudo mkdir -p /mnt/dockerdata/appservice/{redis,postgresql,iotdb,adminapi}
sudo mkdir -p /mnt/dockerdata/appservice/{redis/{outdata,conf},postgresql/data,iotdb/{conf,data,logs},adminapi/{conf,logs}}
sudo mkdir -p /mnt/dockerdata/appservice/{redis,mysql,iotdb,adminapi}
sudo mkdir -p /mnt/dockerdata/appservice/{redis/{outdata,conf},mysql/{conf,log,data},iotdb/{conf,data,logs},adminapi/{conf,logs}}
# 创建本地目录的软链接(如果不存在)
echo "🔗 创建本地目录链接..."
mkdir -p ./redis/{outdata,conf}
mkdir -p ./postgresql/{data,init}
mkdir -p ./mysql/{conf,log,data}
mkdir -p ./iotdb/{conf,data,logs}
mkdir -p ./adminapi/{conf,logs}
# 修复PostgreSQL目录权限
echo "🔧 修复PostgreSQL目录权限..."
sudo chown -R 999:999 ./postgresql/data 2>/dev/null || true
sudo chmod -R 755 ./postgresql/data 2>/dev/null || true
# 修复MySQL目录权限
echo "🔧 修复MySQL目录权限..."
sudo chown -R 1000:1000 ./mysql/data
sudo chown -R 1000:1000 ./mysql/log
sudo chmod -R 755 ./mysql/data
sudo chmod -R 755 ./mysql/log
sudo chmod -R 755 ./adminapi/logs
@ -99,21 +101,19 @@ sudo rm -rf ./iotdb/data/*
sudo rm -rf ./iotdb/logs/*
echo "✅ IoTDB数据目录已清理配置目录已保留"
# PostgreSQL配置检查
echo "⚙️ 检查PostgreSQL配置..."
if [ ! -d "./postgresql/init" ]; then
echo "⚠️ PostgreSQL初始化脚本目录不存在将创建目录可选"
mkdir -p ./postgresql/init
# MySQL配置检查
echo "⚙️ 检查MySQL配置..."
if [ ! -d "./mysql/init" ]; then
echo "❌ MySQL初始化脚本目录不存在请检查"
exit 1
fi
# PostgreSQL初始化脚本是可选的不强制要求
if [ -f "./postgresql/init/init-db.sql" ]; then
echo "✅ PostgreSQL初始化脚本已存在"
else
echo " PostgreSQL初始化脚本不存在可选将在首次启动时使用环境变量创建数据库"
if [ ! -f "./mysql/init/init-db.sql" ]; then
echo "❌ MySQL初始化脚本不存在请检查"
exit 1
fi
echo "✅ PostgreSQL配置检查通过"
echo "✅ MySQL配置检查通过"
# 处理 Admin API 配置文件
echo "📝 配置 Admin API 配置文件..."
@ -157,7 +157,7 @@ sleep 10
echo "IoTDB服务已就绪开始创建表模型数据库和设置root账号密码..."
# 使用非交互式方式创建表模型数据库
echo "CREATE DATABASE jisheiotdata;USE jisheiotdata;CREATE TABLE IF NOT EXISTS CTWingAepReceiveMessageEntity( time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, PlatformTenantId STRING FIELD, ProductId STRING FIELD, ServiceId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD, IMSI STRING FIELD, IMEI STRING FIELD ) COMMENT 'CTWingAepReceiveMessageEntity' ;CREATE TABLE IF NOT EXISTS DeviceTelemetryPacketTaskInfo( time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, IoTPlatformProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, IoTPlatformProductName STRING FIELD, IoTPlatformAccountId STRING FIELD, AccountPhoneNumber STRING FIELD, IssueRawMessage STRING FIELD, IssuePayload STRING FIELD, ResponseRawMessage STRING FIELD, ResponsePayload STRING FIELD, TelemetrySource INT32 FIELD, IoTPlatform INT32 FIELD, RetryCount INT32 FIELD, LastIssueTime TIMESTAMP FIELD, IssueStatus INT32 FIELD ) COMMENT 'DeviceTelemetryPacketTaskInfo' ;CREATE TABLE IF NOT EXISTS OneNETReceiveMessageEntity( time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, ProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, IsEncrypted BOOLEAN FIELD, PlaintextMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD ) COMMENT 'OneNETReceiveMessageEntity'" | ./start-cli.sh -h iotdb-standalone -sql_dialect table
echo "CREATE TABLE IF NOT EXISTS CTWingAepReceiveMessageEntity(time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, SubDevice STRING TAG, PlatformTenantId STRING FIELD, ProductId STRING FIELD, ServiceId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD, IMSI STRING FIELD, IMEI STRING FIELD ) COMMENT 'CTWingAepReceiveMessageEntity';CREATE TABLE IF NOT EXISTS DeviceTelemetryPacketTaskInfo(time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, SubDevice STRING TAG, IoTPlatformProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, IoTPlatformProductName STRING FIELD, IoTPlatformAccountId STRING FIELD, AccountPhoneNumber STRING FIELD, TelemetryType INT32 FIELD, IssueRawMessage STRING FIELD, IssuePayload STRING FIELD, ResponseRawMessage STRING FIELD, ResponsePayload STRING FIELD, TelemetrySource INT32 FIELD, IoTPlatform INT32 FIELD, RetryCount INT32 FIELD, LastIssueTime TIMESTAMP FIELD, IssueStatus INT32 FIELD ) COMMENT 'DeviceTelemetryPacketTaskInfo';CREATE TABLE IF NOT EXISTS OneNETReceiveMessageEntity(time TIMESTAMP TIME, IoTDataType STRING TAG, DeviceType STRING TAG, DeviceAddress STRING TAG, SubDevice STRING TAG, ProductId STRING FIELD, IoTPlatformDeviceOpenInfo STRING FIELD, MessageType STRING FIELD, Protocol STRING FIELD, RawMessage STRING FIELD, IsEncrypted BOOLEAN FIELD, PlaintextMessage STRING FIELD, ReceivedPayload STRING FIELD, ReceivedTimestamps INT64 FIELD ) COMMENT 'OneNETReceiveMessageEntity';" | ./start-cli.sh -h iotdb-standalone -sql_dialect table
# 使用非交互式方式设置密码
echo "ALTER USER root SET PASSWORD '${IOTDB_ROOT_PASSWORD}';" | ./start-cli.sh -h iotdb-standalone
@ -171,7 +171,7 @@ echo ""
echo "📋 连接信息:"
echo " IoTDB: ${SERVER_IP}:${IOTDB_PORT} (root/${IOTDB_ROOT_PASSWORD})"
echo " Redis: ${SERVER_IP}:6379"
echo " PostgreSQL: ${SERVER_IP}:${POSTGRES_PORT:-5432} (postgres/${POSTGRES_PASSWORD:-JiShe!aqG#5kGgh&0})"
echo " MySQL: ${SERVER_IP}:${MYSQL_PORT}"
echo " Admin API: http://${SERVER_IP}:${ADMIN_API_PORT}"
echo " Admin UI: http://${SERVER_IP}:${ADMIN_WEB_PORT}"
echo ""
@ -182,7 +182,7 @@ echo " 停止服务: docker compose down"
echo ""
echo "🧪 测试连接:"
echo " Redis: redis-cli -h ${SERVER_IP} -p 6379 ping"
echo " PostgreSQL: PGPASSWORD=${POSTGRES_PASSWORD:-JiShe!aqG#5kGgh&0} psql -h ${SERVER_IP} -p ${POSTGRES_PORT:-5432} -U postgres -d ${POSTGRES_DB:-jisheiotprodb} -c 'SELECT 1'"
echo " MySQL: mysql -h ${SERVER_IP} -P ${MYSQL_PORT} -uroot -p${MYSQL_ROOT_PASSWORD} -e 'SELECT 1'"
echo " IoTDB: docker exec -it iotdb-standalone ./start-cli.sh -h iotdb-standalone -u root -p Lixiao@1980"
echo ""
echo "🎯 等待服务启动完成..."

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.0.11217.181
VisualStudioVersion = 18.0.11217.181 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.IoT.Domain", "src\JiShe.IoT.Domain\JiShe.IoT.Domain.csproj", "{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}"
EndProject
@ -275,7 +275,6 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DockerComposeShells", "DockerComposeShells", "{8FE7CFC6-BA2A-4717-941C-0C227EB151B9}"
ProjectSection(SolutionItems) = preProject
DockerComposeShells\.env = DockerComposeShells\.env
DockerComposeShells\postgresql\init\01-jisheiotprodb.sql = DockerComposeShells\postgresql\init\01-jisheiotprodb.sql
DockerComposeShells\adminapi\conf\appsettings.Development.json = DockerComposeShells\adminapi\conf\appsettings.Development.json
DockerComposeShells\adminapi\conf\appsettings.Development_服务订阅模式.json = DockerComposeShells\adminapi\conf\appsettings.Development_服务订阅模式.json
DockerComposeShells\adminapi\conf\appsettings.json = DockerComposeShells\adminapi\conf\appsettings.json
@ -292,14 +291,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DockerComposeShells", "Dock
DockerComposeShells\start-service-sub.sh = DockerComposeShells\start-service-sub.sh
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.ServicePro.Shared.DynamicAnalyzerEngine", "JiShe.ServicePro\shared\JiShe.ServicePro.Shared.DynamicAnalyzerEngine\JiShe.ServicePro.Shared.DynamicAnalyzerEngine.csproj", "{539D0149-B52C-7FE1-373D-67EC401D5B47}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.ServicePro.DynamicAnalyzerEngine", "JiShe.ServicePro\frameworks\src\JiShe.ServicePro.DynamicAnalyzerEngine\JiShe.ServicePro.DynamicAnalyzerEngine.csproj", "{6CDFFB25-15FF-19C2-D4AB-921CF40DB056}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.ServicePro.DynamicAnalyzer.Provider", "JiShe.ServicePro\frameworks\src\JiShe.ServicePro.DynamicAnalyzer.Provider\JiShe.ServicePro.DynamicAnalyzer.Provider.csproj", "{516CAE8B-3EDC-8B5C-D15C-5BF9ABF971D8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.ServicePro.EMQXProvider", "JiShe.ServicePro\frameworks\src\JiShe.ServicePro.EMQXProvider\JiShe.ServicePro.EMQXProvider.csproj", "{6DB1C8C3-E57A-005D-D1D0-2194F6AC6537}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -762,22 +753,6 @@ Global
{C51F35A8-5BF8-8E2F-96B2-B62E3BCF8273}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C51F35A8-5BF8-8E2F-96B2-B62E3BCF8273}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C51F35A8-5BF8-8E2F-96B2-B62E3BCF8273}.Release|Any CPU.Build.0 = Release|Any CPU
{539D0149-B52C-7FE1-373D-67EC401D5B47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{539D0149-B52C-7FE1-373D-67EC401D5B47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{539D0149-B52C-7FE1-373D-67EC401D5B47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{539D0149-B52C-7FE1-373D-67EC401D5B47}.Release|Any CPU.Build.0 = Release|Any CPU
{6CDFFB25-15FF-19C2-D4AB-921CF40DB056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CDFFB25-15FF-19C2-D4AB-921CF40DB056}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CDFFB25-15FF-19C2-D4AB-921CF40DB056}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CDFFB25-15FF-19C2-D4AB-921CF40DB056}.Release|Any CPU.Build.0 = Release|Any CPU
{516CAE8B-3EDC-8B5C-D15C-5BF9ABF971D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{516CAE8B-3EDC-8B5C-D15C-5BF9ABF971D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{516CAE8B-3EDC-8B5C-D15C-5BF9ABF971D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{516CAE8B-3EDC-8B5C-D15C-5BF9ABF971D8}.Release|Any CPU.Build.0 = Release|Any CPU
{6DB1C8C3-E57A-005D-D1D0-2194F6AC6537}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6DB1C8C3-E57A-005D-D1D0-2194F6AC6537}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6DB1C8C3-E57A-005D-D1D0-2194F6AC6537}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6DB1C8C3-E57A-005D-D1D0-2194F6AC6537}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -907,10 +882,6 @@ Global
{6A26D2DE-215B-CB11-F882-3984B2EE7AA6} = {72493AF2-CDAA-40A7-98AB-B8AA2B05965E}
{C51F35A8-5BF8-8E2F-96B2-B62E3BCF8273} = {72493AF2-CDAA-40A7-98AB-B8AA2B05965E}
{8FE7CFC6-BA2A-4717-941C-0C227EB151B9} = {2C4A6DB8-8D9E-42E6-B7C3-1EDB7B3DE22E}
{539D0149-B52C-7FE1-373D-67EC401D5B47} = {E10B1433-8E8E-4163-AC98-1058004980F9}
{6CDFFB25-15FF-19C2-D4AB-921CF40DB056} = {72493AF2-CDAA-40A7-98AB-B8AA2B05965E}
{516CAE8B-3EDC-8B5C-D15C-5BF9ABF971D8} = {72493AF2-CDAA-40A7-98AB-B8AA2B05965E}
{6DB1C8C3-E57A-005D-D1D0-2194F6AC6537} = {72493AF2-CDAA-40A7-98AB-B8AA2B05965E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F}

@ -1 +1 @@
Subproject commit cbf9b87d348e3af5dcbe55fc70e03dad91dc69d2
Subproject commit 323492e10b2937a063c9e60b26bc40c6954503c7

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>

View File

@ -6,10 +6,7 @@ using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;
using Volo.Abp.Autofac;
using Volo.Abp.BlobStoring.FileSystem;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.Swashbuckle;
namespace JiShe.IoT
{
@ -22,12 +19,12 @@ namespace JiShe.IoT
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpAccountWebModule),
typeof(IoTApplicationModule),
typeof(AbpAspNetCoreMvcModule),
// typeof(AbpProCapModule),
// typeof(AbpProCapEntityFrameworkCoreModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpCachingStackExchangeRedisModule),
typeof(AbpBlobStoringFileSystemModule),
typeof(AbpDistributedLockingModule),
typeof(AbpEntityFrameworkCorePostgreSqlModule)
typeof(AbpDistributedLockingModule)
//typeof(AbpBackgroundJobsHangfireModule)
)]
public partial class IoTHttpApiHostModule : AbpModule
@ -91,7 +88,7 @@ namespace JiShe.IoT
app.UseSwagger();
app.UseAbpSwaggerUI(options =>
{
var groupNames = System.EnumExtensions.GetEnumTypeValueNameList<SwaggerGroupEnum>();
var groupNames = EnumExtensions.GetEnumTypeValueNameList<SwaggerGroupEnum>();
var excludeSwaggerGroups = configuration.GetSection("ExcludeSwaggerGroup").Get<List<string>>() ?? new List<string>();
groupNames.ForEach(attr =>
{

View File

@ -20,6 +20,7 @@
<PackageReference Include="Volo.Abp.Autofac" />
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" />
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" />
<PackageReference Include="Volo.Abp.Swashbuckle" />
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" />
<PackageReference Include="Serilog" />
@ -32,6 +33,7 @@
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Serilog.Sinks.Async" />
<PackageReference Include="Hangfire.Redis.StackExchange" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" />
<PackageReference Include="DotNetCore.CAP.MySql" />
<PackageReference Include="DotNetCore.CAP.RabbitMQ" />
@ -43,9 +45,6 @@
<PackageReference Include="Volo.Abp.DistributedLocking" />
<PackageReference Include="DistributedLock.Redis" />
<PackageReference Include="Volo.Abp.BlobStoring.FileSystem" />
<PackageReference Include="Volo.Abp.Swashbuckle" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" />
</ItemGroup>
<ItemGroup>
@ -87,6 +86,9 @@
</ItemGroup>
<ItemGroup>
<Content Update="configs\appsettings - 复制.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="configs\appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

View File

@ -53,10 +53,16 @@ public class Program
await app.RunAsync();
return 0;
}
catch (Exception)
catch (Exception ex)
{
if (ex is HostAbortedException)
{
throw;
}
Log.Fatal(ex, "Host terminated unexpectedly!");
return 1;
}
finally
{
await Log.CloseAndFlushAsync();

View File

@ -1,23 +1,22 @@
{
"App": {
"SelfUrl": "http://localhost:44315",
"CorsOrigins": "https://*.IoT.com,http://localhost:4200,http://localhost:3100,http://localhost:80,http://10.10.10.104:4200"
"CorsOrigins": "https://*.IoT.com,http://localhost:4200,http://localhost:3100,http://localhost:80,http://10.10.90.3:4200"
},
"ConnectionStrings": {
//"Default": "Data Source=192.168.111.174;Port=13306;Database=JiSheIoTProDB386;uid=root;pwd=JiShe!aqG#5kGgh&0;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;TreatTinyAsBoolean=false;SslMode=None;Pooling=true;"
"Default": "Host=192.168.111.174;Port=5432;Database=jisheiotprodb;Username=postgres;Password=JiShe!aqG#5kGgh&0;Encoding=UTF8;Pooling=true;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=100;"
"Default": "Data Source=localhost;Port=33306;Database=JiSheIoTProDB54;uid=root;pwd=123456789;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;TreatTinyAsBoolean=false;SslMode=None;Pooling=true;"
},
"Hangfire": {
"Redis": {
"Host": "192.168.111.174:30712,password=1q3J@BGf!yhTaD46nS#",
"Host": "47.110.53.196:30712,password=1q3J@BGf!yhTaD46nS#",
"DB": "2"
}
},
"Redis": {
"Configuration": "192.168.111.174:30712,password=1q3J@BGf!yhTaD46nS#,defaultdatabase=5"
"Configuration": "47.110.53.196:30712,password=1q3J@BGf!yhTaD46nS#,defaultdatabase=5"
},
"Kafka": {
"BootstrapServers": "47.110.62.104:9094,192.168.111.174:9094,47.110.60.222:9094",
"BootstrapServers": "47.110.62.104:9094,47.110.53.196:9094,47.110.60.222:9094",
"EnableFilter": true,
"EnableAuthorization": false,
"SaslUserName": "lixiao",
@ -27,8 +26,8 @@
"TaskThreadCount": -1
},
"Pulsar": {
"ServiceUrl": "pulsar://192.168.111.174:26974", //pulsar+ssl://iot-north-mq.heclouds.com:6651 pulsar://192.168.111.174:26974
"WebUrl": "http://192.168.111.174:9094",
"ServiceUrl": "pulsar+ssl://iot-north-mq.heclouds.com:6651", //pulsar+ssl://iot-north-mq.heclouds.com:6651 pulsar://47.110.53.196:26974
"WebUrl": "http://47.110.53.196:9094",
"UserName": "admin",
"TenantName": "1YMVZZkAkRArjxSD8457",
"Namespace": "OneNET",
@ -40,14 +39,14 @@
"ConnectionTimeout": 30,
"OperationTimeout": 30,
"KeepAliveInterval": 30,
"TaskThreadCount": 1,
"TaskThreadCount": 8,
"IsSubscriber": true,
"DefaultPartitions": 16,
"DefaultBundles": 16,
"EnableAutoCreation": true, //Topic
"TopicMode": "Static", //DynamicStatic
"EnableAutoCreation": false, //Topic
"TopicMode": "Dynamic", //Dynamic
"EnableTopicTypeFilter": true, //Topic
"AllowedTopicTypes": [ "Static" ], //Topic
"AllowedTopicTypes": [ "Dynamic" ], //Topic
"AllowedClusters": [ "pulsar-cluster-1" ], //
"AdminRoles": [ "admin" ],
"EnableConsumerIdleCleanup": true,
@ -57,11 +56,11 @@
},
"IoTDBOptions": {
"UserName": "root",
"Password": "Li#xiao@198!0",
//"TreeModelClusterList": [ "192.168.111.174:6667", "47.110.60.222:6667", "47.110.62.104:6667" ],
//"TableModelClusterList": [ "192.168.111.174:6667", "47.110.60.222:6667", "47.110.62.104:6667" ],
"TreeModelClusterList": [ "192.168.111.174:30710" ],
"TableModelClusterList": [ "192.168.111.174:30710" ],
"Password": "Lixiao@1980",
//"TreeModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ],
//"TableModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ],
"TreeModelClusterList": [ "47.110.53.196:30710" ],
"TableModelClusterList": [ "47.110.53.196:30710" ],
"PoolSize": 32,
"DataBaseName": "jisheiotdata",
"OpenDebugMode": true,
@ -81,9 +80,7 @@
"AesSecurityKey": "RPTEIGCA1KvDEXS1",
"IsAesEncrypted": false,
"DistributedMessage": 2,
"SnowflakeWorkerId": 1,
"DownloadDeviceFirmwareBasicUrl": "http://121.42.175.177:32580/Aggregation/Device/DownloadFirmware?Id=",
"MQTTServerType": 1
"SnowflakeWorkerId": 1
},
"Jwt": {
"Audience": "JiShe.IoT",
@ -100,7 +97,7 @@
}
},
"FreeRedisOptions": {
"ConnectionString": "192.168.111.174:30712,password=1q3J@BGf!yhTaD46nS#,abortConnect=false,connectTimeout=30000,allowAdmin=true,maxPoolSize=500,defaultdatabase=14",
"ConnectionString": "47.110.53.196:30712,password=1q3J@BGf!yhTaD46nS#,abortConnect=false,connectTimeout=30000,allowAdmin=true,maxPoolSize=500,defaultdatabase=14",
"UseDistributedCache": true
},
"FreeSqlProviderOptions": {

View File

@ -2,8 +2,7 @@
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File",
"Serilog.Sinks.Grafana.Loki"
"Serilog.Sinks.File"
],
"MinimumLevel": {
"Default": "Warning",
@ -22,27 +21,10 @@
"Name": "Console"
},
{
"Name": "GrafanaLoki",
"Name": "File",
"Args": {
"uri": "http://192.168.111.164:3100",
"labels": [
{
"key": "app",
"value": "OneNETAdmin" //
},
{
"key": "environment",
"value": "dev" // dev/staging/prod
},
{
"key": "version",
"value": "1.2.3" //
}
],
"propertiesAsLabels": [
"Level",
"SourceContext"
]
"path": "logs/logs-.txt",
"rollingInterval": "Hour"
}
}
]

View File

@ -1,40 +0,0 @@
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.OpenAPIModels;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.BusinessSystemAggregation.Dto
{
/// <summary>
/// 业务系统批量创建设备信息
/// </summary>
public class BatchCreateDeviceBusinessSystemInput
{
/// <summary>
/// 设备信息集合
/// </summary>
[Required(ErrorMessage = "设备信息不能为空")]
public List<OpenApiDeviceInfoInput> DeviceInfos { get; set; }
/// <summary>
/// 物联网平台类型
/// </summary>
[Required(ErrorMessage = "物联网平台类型不能为空")]
public IoTPlatformTypeEnum IoTPlatform { get; set; }
/// <summary>
/// 集中器在物联网平台中对应的产品Id
/// </summary>
[Required(ErrorMessage = "产品Id不能为空")]
public string IoTPlatformProductId { get; set; }
/// <summary>
/// 设备类型,用来区分主设备是网关设备还是直连设备
/// </summary>
public DeviceTypeEnum? DeviceType { get; set; }
/// <summary>
/// 设备来源类型
/// </summary>
public DeviceSourceTypeEnum? DeviceSourceType { get; set; }
}
}

View File

@ -0,0 +1,36 @@
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.BusinessSystemAggregation.Dto
{
/// <summary>
/// 批量创建设备信息输入
/// </summary>
public class BatchCreateDeviceInfoInput
{
/// <summary>
/// 设备来源类型,只接收 预付费业务系统推送、能耗业务系统推送
/// </summary>
public DeviceSourceTypeEnum DeviceSourceType { get; set; }
/// <summary>
/// 物联网平台类型
/// </summary>
public IoTPlatformTypeEnum IoTPlatform { get; set; }
/// <summary>
/// 集中器在物联网平台中对应的产品Id
/// </summary>
public string IoTPlatformProductId { get; set; }
/// <summary>
/// 设备地址集合
/// </summary>
public List<string> DeviceAddresses { get; set; }
}
}

View File

@ -1,45 +0,0 @@
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.BusinessSystemAggregation.Dto
{
/// <summary>
/// 单个查询设备信息输入
/// </summary>
public class QueryDeviceDataInfoInput
{
/// <summary>
/// 设备类型
/// </summary>
public DeviceTypeEnum DeviceType { get; set; }
/// <summary>
/// 数据类型
/// </summary>
public string IoTDataType { get; set; }
/// <summary>
/// 开始时间,最终需要转换为纳秒级时间戳
/// </summary>
public DateTime BeginTime { get; set; }
/// <summary>
/// 结束时间,最终需要转换为纳秒级时间戳
/// </summary>
public DateTime EndTime { get; set; }
/// <summary>
/// 网关或者直连设备地址
/// </summary>
public string DeviceAddress { get; set; }
/// <summary>
/// 子设备地址
/// </summary>
public string SubDeviceAddress { get; set; }
}
}

View File

@ -14,18 +14,11 @@ namespace JiShe.IoT.BusinessSystemAggregation
public interface IBusinessSystemAggregationService : IApplicationService
{
/// <summary>
/// 接收业务系统设置指令信息Msg 字段为 ReceiveCommandInfoDto 实体,操作类型必须有对应的事件上报,进行业务系统回调触发,不能直接使用同步操作。
/// 接收业务系统指令信息Msg 字段为 ReceiveCommandInfoDto 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<HttpDataResult> ReceiveSetCommandInfoAsync(OpenApiRequest input);
/// <summary>
/// 接收业务系统获取属性指令信息Msg 字段为 ReceiveCommandInfoDto 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<HttpDataResult<Dictionary<string, object>>> ReceiveGetCommandInfoAsync(OpenApiRequest input);
Task<HttpDataResult> ReceiveCommandInfoAsync(OpenApiRequest input);
/// <summary>
/// 业务系统批量查询设备数据Msg 字段为 BatchQueryDeviceDataInfoInput 实体
@ -35,14 +28,7 @@ namespace JiShe.IoT.BusinessSystemAggregation
Task<HttpDataResult<List<IoTDBDynamicObject>>> BatchQueryDeviceDataInfoAsync(OpenApiRequest input);
/// <summary>
/// 业务系统查询单个设备数据Msg 字段为 QueryDeviceDataInfoInput 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<HttpDataResult<List<IoTDBDynamicObject>>> QueryDeviceDataInfoAsync(OpenApiRequest input);
/// <summary>
/// 业务系统批量新增设备信息Msg 字段为 BatchCreateDeviceBusinessSystemInput 实体
/// 业务系统批量新增设备数据Msg 字段为 BatchCreateDeviceInfoInput 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>

View File

@ -1,5 +1,4 @@
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.OpenAPIModels;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.DeviceAggregation
@ -10,16 +9,11 @@ namespace JiShe.IoT.DeviceAggregation
public class BatchCreateDeviceAggregationInput
{
/// <summary>
/// 设备地址不能为空
/// 表通信地址集合
/// </summary>
[Required(ErrorMessage = "设备地址不能为空")]
public List<string> AddressList { get; set; }
/// <summary>
/// 设备信息
/// </summary>
public List<OpenApiDeviceInfoInput> DeviceInfos { get; set; }
/// <summary>
/// 物联网平台类型
/// </summary>
@ -36,11 +30,6 @@ namespace JiShe.IoT.DeviceAggregation
/// 设备来源类型
/// </summary>
public DeviceSourceTypeEnum? DeviceSourceType { get; set; }
/// <summary>
/// 设备类型
/// </summary>
public DeviceTypeEnum? DeviceType { get; set; }
public DeviceSourceTypeEnum? DeviceSourceTypeEnum { get; set; }
}
}

View File

@ -1,5 +1,4 @@
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.OpenAPIModels;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.DeviceAggregation.Dto
@ -15,11 +14,6 @@ namespace JiShe.IoT.DeviceAggregation.Dto
[Required(ErrorMessage = "设备地址不能为空")]
public string DeviceAddress { get; set; }
/// <summary>
/// 设备信息
/// </summary>
public OpenApiDeviceInfoInput DeviceInfos { get; set; }
/// <summary>
/// 物联网平台类型
/// </summary>
@ -36,13 +30,7 @@ namespace JiShe.IoT.DeviceAggregation.Dto
/// 设备来源类型
/// </summary>
public DeviceSourceTypeEnum? DeviceSourceType { get; set; }
/// <summary>
/// 设备类型,与业务系统无关
/// </summary>
public DeviceTypeEnum? DeviceType { get; set; }
public DeviceSourceTypeEnum? DeviceSourceTypeEnum { get; set; }
}
}

View File

@ -1,40 +0,0 @@
using JiShe.ServicePro.Enums;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.DeviceAggregation.Dto
{
/// <summary>
/// 设备批量升级信息
/// </summary>
public class DeviceBatchUpgradeForApiInput
{
/// <summary>
/// 设备地址列表
/// </summary>
[Required]
public List<string> AddressList { get; set; }
/// <summary>
/// 物联网平台类型,默认没有指定
/// </summary>
[Required]
public IoTPlatformTypeEnum IoTPlatform { get; set; }
/// <summary>
/// 物联网平台中对应的产品Id
/// </summary>
[Required]
public string IoTPlatformProductId { get; set; }
/// <summary>
/// 固件版本信息
/// </summary>
[Required]
public Guid NowFirmwareVersionDataId { get; set; }
/// <summary>
/// 升级描述
/// </summary>
public string UpgradeDescription { get; set; }
}
}

View File

@ -11,9 +11,19 @@ namespace JiShe.IoT.DeviceAggregation
public class DeviceCommandForApiInput:IdInput
{
/// <summary>
/// 设备在物联网平台中属性设置的参数键值对
/// 设备在物联网平台中发送的命令内容JSON格式
/// </summary>
[Required(ErrorMessage = "命令内容不能为空")]
public Dictionary<string,object> CommandContent { get; set; }
public string CommandContent { get; set; }
/// <summary>
/// 设备类型
/// </summary>
public DeviceTypeEnum? DeviceType { get; set; }
/// <summary>
/// 设备指令类型
/// </summary>
public DeviceTelemetryCommandTypeEnum? TelemetryType { get; set; }
}
}

View File

@ -1,19 +0,0 @@
using JiShe.ServicePro.Core;
using JiShe.ServicePro.Enums;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.DeviceAggregation
{
/// <summary>
/// 设备属性抄读
/// </summary>
public class DevicePropertyValueForApiInput : IdInput
{
/// <summary>
/// 设备在物联网平台中属性标识符列表
/// </summary>
[Required(ErrorMessage = "属性标识符不能为空")]
public List<string> PropertyList { get; set; }
}
}

View File

@ -1,36 +0,0 @@
using JiShe.ServicePro.Core;
using JiShe.ServicePro.Enums;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.DeviceAggregation
{
/// <summary>
/// 设备升级
/// </summary>
public class DeviceUpgradeForApiInput : IdInput
{
/// <summary>
/// 物联网平台类型,默认没有指定
/// </summary>
[Required]
public IoTPlatformTypeEnum IoTPlatform { get; set; }
/// <summary>
/// 物联网平台中对应的产品Id
/// </summary>
[Required]
public string IoTPlatformProductId { get; set; }
/// <summary>
/// 固件版本信息
/// </summary>
[Required]
public Guid NowFirmwareVersionDataId { get; set; }
/// <summary>
/// 升级描述
/// </summary>
public string UpgradeDescription { get; set; }
}
}

View File

@ -1,12 +1,8 @@
using JiShe.IoT.BusinessSystemAggregation.Dto;
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.ServicePro;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.DeviceManagement.DeviceInfos;
using JiShe.ServicePro.DeviceManagement.DeviceInfos.Dto;
using JiShe.ServicePro.FileManagement.Files;
using Volo.Abp;
using Volo.Abp.Content;
namespace JiShe.IoT.DeviceAggregation
{
@ -50,32 +46,6 @@ namespace JiShe.IoT.DeviceAggregation
/// <returns></returns>
Task<bool> DeviceCommandForApiAsync(DeviceCommandForApiInput input);
/// <summary>
/// 发送设备升级指令信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<bool> DeviceUpgradeForApiAsync(DeviceUpgradeForApiInput input);
/// <summary>
/// 批量发送设备升级指令信息,只对同一个平台下,同一个产品下的设备进行固件升级信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<List<string>> DeviceBatchUpgradeForApiAsync(DeviceBatchUpgradeForApiInput input);
/// <summary>
/// 下载设备固件文件
/// </summary>
Task<RemoteStreamContent> DownloadFirmwareInfoAsync(IdInput input);
/// <summary>
/// 获取设备属性最新值
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<Dictionary<string,object>> GetDevicePropertyValueForApiAsync(DevicePropertyValueForApiInput input);
/// <summary>
/// 删除设备信息
/// </summary>
@ -101,10 +71,10 @@ namespace JiShe.IoT.DeviceAggregation
Task<DeviceManagementInfoDto> RepushDeviceInfoToIoTPlatform(IdInput input);
/// <summary>
/// 绑定设备端物模型
/// 业务系统批量创建设备信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<bool> BindingDeviceThingModel(BindingDeviceThingModelInput input);
Task<bool> BatchCreateDeviceBusinessSystemAsync(BatchCreateDeviceAggregationInput input);
}
}

View File

@ -1,22 +0,0 @@
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.IoTPlatformAggregation.Dto
{
/// <summary>
/// 平台账号信息输入
/// </summary>
public class IoTPlatformAccountInfoInput
{
/// <summary>
/// 平台类型
/// </summary>
[Required]
public IoTPlatformTypeEnum IoTPlatformType { get; set; }
}
}

View File

@ -1,31 +0,0 @@
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.IoTPlatformAggregation.Dto
{
/// <summary>
/// 平台账号信息
/// </summary>
public class IoTPlatformAccountInfoOutput
{
/// <summary>
/// 物联网平台类型
/// </summary>
public IoTPlatformTypeEnum IoTPlatformType { get; set; }
/// <summary>
/// 平台账号
/// </summary>
public string IoTPlatformAccount { get; set; }
/// <summary>
/// 平台账号手机号
/// </summary>
public string IoTPlatformPhoneNumber { get; set; }
}
}

View File

@ -1,27 +0,0 @@
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.IoTPlatformAggregation.Dto
{
/// <summary>
/// 产品信息输入
/// </summary>
public class IoTPlatformProductInfoInput
{
/// <summary>
/// 平台类型
/// </summary>
[Required]
public IoTPlatformTypeEnum IoTPlatformType { get; set; }
/// <summary>
/// 平台账号
/// </summary>
public string IoTPlatformAccount { get; set; }
}
}

View File

@ -1,31 +0,0 @@
using JiShe.ServicePro.Enums;
using Swashbuckle.AspNetCore.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.IoTPlatformAggregation.Dto
{
/// <summary>
/// 平台产品信息
/// </summary>
public class IoTPlatformProductInfoOutput
{
/// <summary>
/// 物联网平台类型
/// </summary>
public IoTPlatformTypeEnum IoTPlatformType { get; set; }
/// <summary>
/// 物联网平台对应的产品Id
/// </summary>
public string IoTPlatformProductId { get; set; }
/// <summary>
/// 产品名称
/// </summary>
public string ProductName { get; set; }
}
}

View File

@ -1,34 +0,0 @@
using JiShe.ServicePro.Core;
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.IoTPlatformAggregation.Dto
{
/// <summary>
/// 产品属性信息输入
/// </summary>
public class IoTPlatformProductThingModelInfoInput
{
/// <summary>
/// 物联网平台类型,默认没有指定
/// </summary>
[Required]
public IoTPlatformTypeEnum IoTPlatformType { get; set; }
/// <summary>
/// 物联网平台中对应产品物模型属性或者事件类型 <see cref="DataDictionaryTypeConst"/>
/// </summary>
public string FiledType { get; set; }
/// <summary>
/// 物联网平台中对应的产品Id
/// </summary>
[Required]
public string IoTPlatformProductId { get; set; }
}
}

View File

@ -1,29 +0,0 @@
using JiShe.ServicePro.Core;
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.IoTPlatformAggregation.Dto
{
/// <summary>
/// 平台产品属性更新输入
/// </summary>
public class UpdateIoTPlatformProductThingModelInfoInput
{
/// <summary>
/// 物联网平台类型,默认没有指定
/// </summary>
[Required]
public IoTPlatformTypeEnum IoTPlatformType { get; set; }
/// <summary>
/// 物联网平台中对应的产品Id
/// </summary>
[Required]
public string IoTPlatformProductId { get; set; }
}
}

View File

@ -1,49 +0,0 @@
using JiShe.IoT.IoTPlatformAggregation.Dto;
using JiShe.ServicePro.Commons;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.IoTPlatformAggregation
{
/// <summary>
/// 平台聚合服务
/// </summary>
public interface IIoTPlatformAggregationService
{
/// <summary>
/// 获取平台产品信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<List<IoTPlatformProductInfoOutput>> GetIoTPlatformProductInfoAsync(IoTPlatformProductInfoInput input
);
/// <summary>
/// 获取平台账号信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<List<IoTPlatformAccountInfoOutput>> GetIoTPlatformAccountInfoAsync(IoTPlatformAccountInfoInput input
);
/// <summary>
/// 获取平台产品物模型属性信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<List<SelectResult>> GetIoTPlatformProductThingModelInfoAsync(IoTPlatformProductThingModelInfoInput input
);
/// <summary>
/// 更新平台产品物模型属性信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<object> UpdateIoTPlatformProductThingModelInfoAsync(UpdateIoTPlatformProductThingModelInfoInput input
);
}
}

View File

@ -1,6 +1,4 @@
using JiShe.ServicePro.Dto;
namespace JiShe.ServicePro.OneNETManagement.Subscribers
namespace JiShe.ServicePro.OneNETManagement.Subscribers
{
/// <summary>
/// 数据通讯通道订阅者服务
@ -12,25 +10,5 @@ namespace JiShe.ServicePro.OneNETManagement.Subscribers
/// </summary>
/// <returns></returns>
Task ServiceCommunicationDeviceStatusSubscriber();
/// <summary>
/// 设备在线状态更新
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task DeviceOnlineStatusUpdate(DeviceStatusMessage input);
/// <summary>
/// 设备升级结果通知 Redis 消息订阅
/// </summary>
/// <returns></returns>
Task ServiceCommunicationDeviceUpgradeSubscriber();
/// <summary>
/// 设备升级结果更新
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task DeviceUpgradeResultUpdate(UpgradeResultMessage input);
}
}

View File

@ -1,5 +1,4 @@
using JiShe.ServicePro;
using JiShe.ServicePro.Enums;
using Swashbuckle.AspNetCore.Annotations;
using System;
using System.Collections.Generic;
@ -15,27 +14,22 @@ namespace JiShe.IoT.Workshops
public class WorkshopProductListOutput
{
/// <summary>
/// 平台类型
/// 产品数据Id
/// </summary>
public IoTPlatformTypeEnum IoTPlatform { get; set; }
/// <summary>
/// 产品Id
/// </summary>
[SwaggerSchema("产品Id")]
public string IoTPlatformProductId { get; set; }
[SwaggerSchema("产品数据Id")]
public Guid ProductDataId { get; set; }
/// <summary>
/// 产品名称
/// </summary>
[SwaggerSchema("产品名称")]
public string IoTPlatformProductName { get; set; }
public string ProductName { get; set; }
/// <summary>
/// 产品访问密钥
/// </summary>
[SwaggerSchema("产品访问密钥")]
public string IoTPlatformProductAccesskey { get; set; }
public string ProductAccesskey { get; set; }
/// <summary>
/// 通讯服务地址

View File

@ -6,97 +6,46 @@ using JiShe.ServicePro.ApacheIoTDB.Provider.Options;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.DataChannelManages;
using JiShe.ServicePro.DeviceManagement.DeviceInfos;
using JiShe.ServicePro.DeviceManagement.ThingModels;
using JiShe.ServicePro.Dto;
using JiShe.ServicePro.Encrypt;
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.FreeRedisProvider;
using JiShe.ServicePro.IoTDBManagement.DataChannels;
using JiShe.ServicePro.IoTDBManagement.TreeModels;
using JiShe.ServicePro.OneNETManagement.OneNETDevices;
using JiShe.ServicePro.OneNETManagement.OneNETProducts;
using Mapster;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Linq;
using Volo.Abp;
using System.Collections.Generic;
using Volo.Abp.Auditing;
using static FreeSql.Internal.GlobalFilter;
using static Volo.Abp.Ui.LayoutHooks.LayoutHooks;
namespace JiShe.IoT.BusinessSystemAggregation
{
/// <summary>
/// 业务系统聚合服务
/// <param name="_logger"></param>
/// <param name="_deviceAppService">设备服务</param>
/// <param name="_oneNETDeviceService">OneNET设备服务</param>
/// <param name="_redisPubSubService">Redis发布订阅服务</param>
/// <param name="_ioTDBDataChannelManageService">数据通道</param>
/// <param name="_ioTDBOptions">IoTDBOptions</param>
/// <param name="_oneNETProductService">OneNET产品服务</param>
/// <param name="_deviceThingModelService">设备端物模型服务</param>
/// <param name="_platformThingModelInfoAppService">平台端端物模型服务</param>
/// <param name="_deviceUpgradeRecordService">设备升级记录服务</param>
/// <param name="_serverOptions">应用服务配置</param>
/// <param name="_treeModelService">IoTDB树模型服务</param>
/// </summary>
[DisableAuditing]
public class BusinessSystemAggregationService(
IDeviceAppService _deviceAppService,
IOneNETDeviceService _oneNETDeviceService,
IReliableRedisPubSubService _redisPubSubService,
IIoTDBDataChannelManageService _ioTDBDataChannelManageService,
IOptions<IoTDBOptions> _ioTDBOptions,
IOptions<ServerApplicationOptions> _serverOptions,
IOneNETProductService _oneNETProductService,
IDeviceThingModelManagementAppService _deviceThingModelService,
IIoTPlatformThingModelInfoAppService _platformThingModelInfoAppService,
IDeviceUpgradeRecordService _deviceUpgradeRecordService,
ITreeModelService _treeModelService,
ILogger<BusinessSystemAggregationService> _logger)
: IoTDeviceBasicAppService(_logger,
_deviceAppService,
_oneNETDeviceService,
_redisPubSubService,
_ioTDBDataChannelManageService,
_ioTDBOptions,
_serverOptions,
_oneNETProductService,
_deviceThingModelService,
_platformThingModelInfoAppService,
_deviceUpgradeRecordService,
_treeModelService), IBusinessSystemAggregationService
public class BusinessSystemAggregationService(IOptions<ServerApplicationOptions> options, IReliableRedisPubSubService redisPubSubService, IDeviceAppService deviceAppService, IIoTDBDataChannelManageService ioTDBDataChannelManageService, IOptions<IoTDBOptions> _ioTDBOptions, ITreeModelService treeModelService, IDeviceAggregationService deviceAggregationService, ILogger<BusinessSystemAggregationService> _logger) : IoTAppService, IBusinessSystemAggregationService
{
private const string LUA_SCRIPT = @"
local hashKey = KEYS[1]
local fieldKeys = ARGV
return redis.call('HMGET', hashKey, unpack(fieldKeys))";
ServerApplicationOptions serverOptions = options.Value;
IoTDBOptions ioTDBOptions = _ioTDBOptions.Value;
/// <summary>
/// 接收业务系统设置指令信息Msg 字段为 ReceiveCommandInfoDto 实体,操作类型必须有对应的事件上报,进行业务系统回调触发,不能直接使用同步操作。
/// 接收业务系统指令信息
/// </summary>
[AllowAnonymous]
public async Task<HttpDataResult> ReceiveSetCommandInfoAsync(OpenApiRequest input)
public async Task<HttpDataResult> ReceiveCommandInfoAsync(OpenApiRequest input)
{
try
{
var handleResult = HandleOpenApiRequest<ReceiveCommandInfoDto>(input, serverApplicationOptions);
var handleResult = HandleOpenApiRequest<ReceiveCommandInfoDto>(input, serverOptions);
if (handleResult.Success == false)
{
return handleResult;
}
var messageBody = handleResult.Data;
if (messageBody == null || messageBody.Commands == null || messageBody.Commands.Count <= 0)
{
return HttpDataResultExtensions.Failed("设备指令不能为空", -103, ResponeResultEnum.Fail);
}
List<DeviceTelemetryCommandTypeEnum> actionTypes = new List<DeviceTelemetryCommandTypeEnum>() {
DeviceTelemetryCommandTypeEnum.OperateBreaker,
DeviceTelemetryCommandTypeEnum.RoutineOperationSet,
};
string tempMessageBody = null;
//限定来源类型必须为业务系统
if (messageBody.SourceType != DeviceTelemetrySourceTypeEnum.BusinessSystem)
@ -104,12 +53,6 @@ namespace JiShe.IoT.BusinessSystemAggregation
return HttpDataResultExtensions.Failed("设备指令来源类型错误业务系统传固定值2", -104, ResponeResultEnum.Fail);
}
//限定操作类型必须为设置类型
if (!actionTypes.Contains(messageBody.TelemetryCommandType))
{
return HttpDataResultExtensions.Failed("设备指令操作类型错误", -105, ResponeResultEnum.Fail);
}
var deviceInfo = await deviceAppService.FindByDeviceAddressAsync(messageBody.DeviceAddress);
if (deviceInfo == null)
@ -117,57 +60,15 @@ namespace JiShe.IoT.BusinessSystemAggregation
return HttpDataResultExtensions.Failed("设备不存在", -1041, ResponeResultEnum.Fail);
}
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, input, deviceInfo.Adapt<DeviceCacheInfos>(), tempMessageBody);
//将指令存储IoTDB数据库和Redis发布通道
if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.OneNET)
{
//获取设备对应的平台端物模型信息,校验前端传入的属性标识集合是否存在不合法的属性标识符
var platformThingModelInfo = await platformThingModelInfoAppService.FindByPlatformProductIdAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
if (platformThingModelInfo == null)
{
throw new UserFriendlyException($"业务系统推送指令时设备{deviceInfo.DeviceAddress}的平台端物模型信息不存在。");
}
Dictionary<string, object> commands = new Dictionary<string, object>();
foreach (var item in messageBody.Commands)
{
var tempPlatformThingModelInfo = platformThingModelInfo.Where(d => d.IoTPlatformRawFieldName == item.Key).FirstOrDefault();
if (tempPlatformThingModelInfo == null)
{
_logger.LogError($"业务系统推送指令时设备设备{deviceInfo.DeviceAddress}平台端物模型信息不存在属性标识符{item.Key}。");
messageBody.Commands.RemoveAll(d => d.Key == item.Key);
continue;
}
if (deviceInfo.IsNeedConfigDeviceModel && deviceInfo.DeviceThingModelDataId.HasValue && item.Key.ToLowerInvariant() == ThingModelFixedTypeConst.SpecialCommand.ToLowerInvariant())
{
_logger.LogError($"业务系统推送指令时设备{deviceInfo.DeviceAddress}平台端物模型属性标识符{item.Key}是特殊指令操作,被禁止。");
messageBody.Commands.RemoveAll(d => d.Key == item.Key);
continue;
}
//排除指令
if (exitCommands.Contains(tempPlatformThingModelInfo.StandardFieldName.ToLowerInvariant()))
{
_logger.LogError($"业务系统推送指令时设备{deviceInfo.DeviceAddress}平台端物模型属性标识符{item.Key}是升级指令操作,被禁止。");
messageBody.Commands.RemoveAll(d => d.Key == item.Key);
continue;
}
commands.Add(tempPlatformThingModelInfo.IoTPlatformRawFieldName, item.Value);
}
if (commands == null || commands.Count <= 0)
{
_logger.LogError($"业务系统推送设置属性指令时设备{deviceInfo.DeviceAddress}未匹配到具体数据模型,被禁止。");
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("设置属性失败,未匹配到具体数据模型", -108);
}
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, input, deviceInfo.Adapt<DeviceCacheInfos>(), commands.Serialize());
//数据写入遥测任务数据存储通道
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo));
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.OneNETCommandIssuedEventName, input);
}
else if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.CTWing)
@ -176,157 +77,14 @@ namespace JiShe.IoT.BusinessSystemAggregation
}
else
{
return HttpDataResultExtensions.Failed("设置属性指令处理失败,当前设备平台类型异常", -105);
return HttpDataResultExtensions.Failed("指令处理失败,当前设备平台类型异常", -105);
}
return HttpDataResultExtensions.Success("指令下发成功");
}
catch (UserFriendlyException)
{
throw; // 重新抛出用户友好异常
}
catch (Exception ex)
{
_logger.LogError(ex, "接收业务系统指令信息时发生异常");
return HttpDataResultExtensions.Failed("指令处理失败,发送异常", -106);
}
}
/// <summary>
/// 接收业务系统获取属性指令信息
/// </summary>
[AllowAnonymous]
public async Task<HttpDataResult<Dictionary<string, object>>> ReceiveGetCommandInfoAsync(OpenApiRequest input)
{
try
{
var handleResult = HandleOpenApiRequest<ReceiveCommandInfoDto>(input, serverApplicationOptions);
if (handleResult.Success == false)
{
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("获取数据失败,签名校验异常", -101);
}
var messageBody = handleResult.Data;
if (messageBody == null || messageBody.Commands == null || messageBody.Commands.Count <= 0 || string.IsNullOrWhiteSpace(messageBody.DeviceAddress))
{
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("获取数据失败,设备属性值不能为空", -102, ResponeResultEnum.Fail);
}
var restrictionKey = string.Format(RedisConst.CacheDeviceOpenApiRestrictionKey,nameof(ReceiveGetCommandInfoAsync), messageBody.DeviceAddress);
var openApiDeviceRequest = FreeRedisProvider.Instance.Get(restrictionKey);
if (openApiDeviceRequest != null)
{
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("获取数据失败,设备请求被限制", -103, ResponeResultEnum.Fail);
}
FreeRedisProvider.Instance.Set(restrictionKey,"1",TimeSpan.FromSeconds(5));
//限定来源类型必须为业务系统
if (messageBody.SourceType != DeviceTelemetrySourceTypeEnum.BusinessSystem)
{
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("获取数据失败来源类型错误业务系统传固定值2", -104, ResponeResultEnum.Fail);
}
//限定操作类型必须为设置类型
if (messageBody.TelemetryCommandType != DeviceTelemetryCommandTypeEnum.GetAttributeData)
{
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("设备指令操作类型错误", -105, ResponeResultEnum.Fail);
}
var deviceInfo = await deviceAppService.FindByDeviceAddressAsync(messageBody.DeviceAddress);
if (deviceInfo == null)
{
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("设备不存在", -106, ResponeResultEnum.Fail);
}
//将指令存储IoTDB数据库和Redis发布通道
if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.OneNET)
{
//获取设备对应的平台端物模型信息,校验前端传入的属性标识集合是否存在不合法的属性标识符
var platformThingModelInfo = await platformThingModelInfoAppService.FindByPlatformProductIdAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
if (platformThingModelInfo == null)
{
return HttpDataResultExtensions.Failed<Dictionary<string, object>>($"业务系统推送指令时设备{deviceInfo.DeviceAddress}的平台端物模型信息不存在。", -107, ResponeResultEnum.Fail);
}
Dictionary<string, string> commands = new Dictionary<string, string>();
foreach (var item in messageBody.Commands)
{
var tempPlatformThingModelInfo = platformThingModelInfo.Where(d => d.StandardFieldName == item.Key).FirstOrDefault();
if (tempPlatformThingModelInfo == null)
{
_logger.LogError($"业务系统推送指令时设备设备{deviceInfo.DeviceAddress}平台端物模型信息不存在属性标识符{item.Key}。");
messageBody.Commands.RemoveAll(d => d.Key == item.Key);
continue;
}
if (deviceInfo.IsNeedConfigDeviceModel && deviceInfo.DeviceThingModelDataId.HasValue && item.Key.ToLowerInvariant() == ThingModelFixedTypeConst.SpecialCommand.ToLowerInvariant())
{
_logger.LogError($"业务系统推送指令时设备{deviceInfo.DeviceAddress}平台端物模型属性标识符{item.Key}是特殊指令操作,被禁止。");
messageBody.Commands.RemoveAll(d => d.Key == item.Key);
continue;
}
//排除指令
if (exitCommands.Contains(tempPlatformThingModelInfo.StandardFieldName.ToLowerInvariant()))
{
_logger.LogError($"业务系统推送指令时设备{deviceInfo.DeviceAddress}平台端物模型属性标识符{item.Key}是升级指令操作,被禁止。");
messageBody.Commands.RemoveAll(d => d.Key == item.Key);
continue;
}
commands.Add(tempPlatformThingModelInfo.IoTPlatformRawFieldName,item.Key);
}
if (commands == null || commands.Count <= 0)
{
_logger.LogError($"业务系统推送指令时设备{deviceInfo.DeviceAddress}未匹配到具体数据模型,被禁止。");
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("获取数据失败,未匹配到具体数据模型", -108);
}
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, input, deviceInfo.Adapt<DeviceCacheInfos>(), commands.Serialize());
//数据写入遥测任务数据存储通道
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo));
var queryResult = await DevicePropertyValueToOneNET(deviceInfo, new DevicePropertyValueForApiInput() { PropertyList = commands.Select(d => d.Key).ToList() });
if (queryResult == null || queryResult.Count <= 0)
{
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("获取数据失败OneNET平台未返回数据", -109);
}
Dictionary<string, object> result = new Dictionary<string, object>();
foreach (var item in commands)
{
result.Add(item.Value, queryResult[item.Key]);
}
return HttpDataResultExtensions.Success<Dictionary<string, object>>(result);
}
else if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.CTWing)
{
//await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.CTWingAepCommandIssuedEventName, input);
}
else
{
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("指令处理失败,当前设备平台类型异常", -110);
}
return HttpDataResultExtensions.Success<Dictionary<string, object>>("指令下发成功");
}
catch (UserFriendlyException ex)
{
_logger.LogError(ex, "接收业务系统指令信息时发生异常");
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("设备属性数据获取失败,发送异常", -111);
}
catch (Exception ex)
{
_logger.LogError(ex, "接收业务系统指令信息时发生异常");
return HttpDataResultExtensions.Failed<Dictionary<string, object>>("设备属性数据获取失败,发送异常", -112);
return HttpDataResultExtensions.Failed($"指令处理失败,发送异常:{ex.Message}", -106);
}
}
@ -341,7 +99,7 @@ namespace JiShe.IoT.BusinessSystemAggregation
{
try
{
var handleResult = HandleOpenApiRequest<BatchQueryDeviceDataInfoInput>(input, serverApplicationOptions);
var handleResult = HandleOpenApiRequest<BatchQueryDeviceDataInfoInput>(input, serverOptions);
if (handleResult.Success == false)
{
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, handleResult.Message, handleResult.LocationCode);
@ -353,19 +111,16 @@ namespace JiShe.IoT.BusinessSystemAggregation
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, "设备地址不能为空", -103);
}
// 验证设备地址格式,防止注入攻击
foreach (var deviceAddress in messageBody.DeviceAddresses)
{
if (!IsValidDeviceAddress(deviceAddress))
{
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, $"设备地址格式不正确: {deviceAddress}", -107);
}
}
// Lua脚本
string luaScript = @"
local hashKey = KEYS[1]
local fieldKeys = ARGV
return redis.call('HMGET', hashKey, unpack(fieldKeys))";
//执行脚本
var result = await FreeRedisProvider.Instance.EvalAsync
(
LUA_SCRIPT,
luaScript,
new[] { RedisConst.CacheAllDeviceInfoHashKey },
messageBody.DeviceAddresses.ToArray()
);
@ -376,24 +131,16 @@ namespace JiShe.IoT.BusinessSystemAggregation
if (result is object[] values)
{
foreach (var value in values)
{
if (value != null)
{
var tempFocusInfo = ServiceProJsonSerializer.Deserialize<DeviceCacheInfos>(value as string);
deviceCacheInfos.Add(tempFocusInfo);
}
else
{
deviceCacheInfos.Add(null); // 添加空值以保持索引对应
}
}
}
List<IoTDBDynamicObject> queryResult = new List<IoTDBDynamicObject>();
for (int i = 0; i < messageBody.DeviceAddresses.Count; i++)
foreach (var deviceAddress in messageBody.DeviceAddresses)
{
var deviceAddress = messageBody.DeviceAddresses[i];
var deviceCacheInfo = deviceCacheInfos.Count > i ? deviceCacheInfos[i] : null;
var deviceCacheInfo = deviceCacheInfos.FirstOrDefault(x => x.DeviceAddress == deviceAddress);
if (deviceCacheInfo == null)
{
@ -424,108 +171,15 @@ namespace JiShe.IoT.BusinessSystemAggregation
}
catch (Exception ex)
{
_logger.LogError(ex, "批量查询设备数据时发生异常");
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, "查询设备数据失败", -106);
}
}
/// <summary>
/// 业务系统查询单个设备数据Msg 字段为 QueryDeviceDataInfoInput 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[AllowAnonymous]
public async Task<HttpDataResult<List<IoTDBDynamicObject>>> QueryDeviceDataInfoAsync(OpenApiRequest input)
{
try
{
var handleResult = HandleOpenApiRequest<QueryDeviceDataInfoInput>(input, serverApplicationOptions);
if (handleResult.Success == false)
{
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, handleResult.Message, handleResult.LocationCode);
}
var messageBody = handleResult.Data;
if (string.IsNullOrWhiteSpace(messageBody.DeviceAddress))
{
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, "设备地址不能为空", -103);
}
// 验证设备地址格式,防止注入攻击
if (!IsValidDeviceAddress(messageBody.DeviceAddress))
{
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, $"设备地址格式不正确: {messageBody.DeviceAddress}", -107);
}
//执行脚本
var result = await FreeRedisProvider.Instance.EvalAsync
(
LUA_SCRIPT,
new[] { RedisConst.CacheAllDeviceInfoHashKey },
new List<string>() { messageBody.DeviceAddress }.ToArray()
);
List<DeviceCacheInfos> deviceCacheInfos = new List<DeviceCacheInfos>();
// 处理返回结果
if (result is object[] values)
{
foreach (var value in values)
{
if (value != null)
{
var tempFocusInfo = ServiceProJsonSerializer.Deserialize<DeviceCacheInfos>(value as string);
deviceCacheInfos.Add(tempFocusInfo);
}
else
{
deviceCacheInfos.Add(null);
}
}
}
List<IoTDBDynamicObject> queryResult = new List<IoTDBDynamicObject>();
var deviceCacheInfo = deviceCacheInfos.FirstOrDefault();
if (deviceCacheInfo == null)
{
_logger.LogError($"{nameof(QueryDeviceDataInfoAsync)} 业务系统单个查询设备数据,设备地址:{messageBody.DeviceAddress}未找到设备地址缓存信息,消息体为:{input.Serialize()}");
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, "设备信息不存在", -108);
}
var pageResult = await treeModelService.OpenRequestDeviceDataInfoPageAsync(new DeviceTreeModelDataInfoInput()
{
DeviceAddress = messageBody.DeviceAddress,
DeviceType = messageBody.DeviceType,
IoTDataType = messageBody.IoTDataType,
IsNeedPaging = false,
StartCreationTime = messageBody.BeginTime,
EndCreationTime = messageBody.EndTime,
SubDeviceAddress = messageBody.SubDeviceAddress,
});
//todo 根据业务系统时间间隔要求进行过滤
if (pageResult.Items != null && pageResult.Items.Count > 0)
{
queryResult.AddRange(pageResult.Items);
}
return HttpDataResultExtensions.Success(queryResult, "查询成功");
}
catch (Exception ex)
{
_logger.LogError(ex, "查询单个设备数据时发生异常");
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, "查询设备数据失败", -106);
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, $"查询设备数据失败,发送异常:{ex.Message}", -106);
}
}
/// <summary>
/// 业务系统批量新增设备信息Msg 字段为 BatchCreateDeviceBusinessSystemInput 实体
/// 业务系统批量新增设备数据Msg 字段为 BatchCreateDeviceInfoInput 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
@ -534,72 +188,38 @@ namespace JiShe.IoT.BusinessSystemAggregation
{
try
{
var handleResult = HandleOpenApiRequest<BatchCreateDeviceBusinessSystemInput>(input, serverApplicationOptions);
var handleResult = HandleOpenApiRequest<BatchCreateDeviceInfoInput>(input, serverOptions);
if (handleResult.Success == false)
{
return HttpDataResultExtensions.Failed(handleResult.Message, handleResult.LocationCode);
}
var messageBody = handleResult.Data;
if (messageBody.DeviceInfos == null || messageBody.DeviceInfos.Count <= 0)
if (messageBody.DeviceAddresses == null || messageBody.DeviceAddresses.Count <= 0)
{
_logger.LogError($"{nameof(BatchCreateDeviceInfoAsync)} 业务系统批量新增设备数据,设备地址不能为空,消息体为:{input.Serialize()}");
return HttpDataResultExtensions.Failed("设备地址不能为空", -101);
}
if (messageBody.DeviceSourceType != DeviceSourceTypeEnum.Prepay && messageBody.DeviceSourceType != DeviceSourceTypeEnum.Energy)
var createResult = await deviceAggregationService.BatchCreateDeviceBusinessSystemAsync(new BatchCreateDeviceAggregationInput()
{
_logger.LogError($"{nameof(BatchCreateDeviceInfoAsync)} 业务系统批量创建设备信息,设备来源异常,消息体为:{input.Serialize()}");
return HttpDataResultExtensions.Failed("业务系统批量创建设备信息,设备来源异常。", -102);
}
var createResult = false;
if (messageBody.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
var batchCreateDeviceInput = input.Adapt<BatchCreateDeviceAggregationInput>();
batchCreateDeviceInput.AddressList = messageBody.DeviceInfos.Select(f => f.DeviceAddress.Trim()).ToList();
createResult = await CTWingDeviceBatchCreateAsync(batchCreateDeviceInput);
}
else if (messageBody.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.OneNET)
{
var batchCreateDeviceInput = input.Adapt<BatchCreateDeviceAggregationInput>();
batchCreateDeviceInput.AddressList = messageBody.DeviceInfos.Select(f => f.DeviceAddress.Trim()).ToList();
createResult = await OneNETDeviceBatchCreateAsync(batchCreateDeviceInput);
}
DeviceSourceTypeEnum = messageBody.DeviceSourceType,
IoTPlatform = messageBody.IoTPlatform,
IoTPlatformProductId = messageBody.IoTPlatformProductId,
AddressList = messageBody.DeviceAddresses
});
if (createResult == false)
{
_logger.LogError($"{nameof(BatchCreateDeviceInfoAsync)} 业务系统批量新增设备数据没有成功,消息体为:{input.Serialize()}");
return HttpDataResultExtensions.Failed("新增设备失败", -104);
return HttpDataResultExtensions.Failed("新增设备失败", -102);
}
return HttpDataResultExtensions.Success("新增设备成功");
}
catch (Exception ex)
{
_logger.LogError(ex, "批量新增设备数据时发生异常");
return HttpDataResultExtensions.Failed("新增设备失败", -106);
return HttpDataResultExtensions.Failed($"查询设备数据失败,发送异常:{ex.Message}", -106);
}
}
/// <summary>
/// 验证设备地址格式是否合法
/// </summary>
/// <param name="deviceAddress">设备地址</param>
/// <returns>是否合法</returns>
private bool IsValidDeviceAddress(string deviceAddress)
{
if (string.IsNullOrWhiteSpace(deviceAddress))
return false;
// 可根据实际业务规则调整验证逻辑
// 这里简单检查是否包含非法字符
return !deviceAddress.Contains("..") &&
!deviceAddress.Contains("*") &&
!deviceAddress.Contains("~");
}
}
}

View File

@ -4,7 +4,6 @@ using JiShe.ServicePro.Core;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using static FreeSql.Internal.GlobalFilter;
namespace JiShe.IoT.CommonServices
@ -20,45 +19,7 @@ namespace JiShe.IoT.CommonServices
public void InitSelectTypetList()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// 排除 Npgsql 和系统程序集
var excludedAssemblies = new[]
{
"Npgsql",
"Npgsql.EntityFrameworkCore.PostgreSQL"
};
var typeList = assemblies
.Where(assembly =>
{
var assemblyName = assembly.GetName().Name ?? "";
// 排除 Npgsql 相关程序集
return !excludedAssemblies.Any(excluded =>
assemblyName.StartsWith(excluded, StringComparison.OrdinalIgnoreCase));
})
.SelectMany(assembly =>
{
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
// 某些类型加载失败,只返回成功加载的类型
return ex.Types.Where(t => t != null);
}
catch (TypeLoadException)
{
// 类型加载异常,跳过这个程序集
return Enumerable.Empty<Type>();
}
catch (Exception)
{
// 其他异常,跳过这个程序集
return Enumerable.Empty<Type>();
}
})
.Where(t => t != null && t.IsDefined(typeof(SelectResultAttribute), false));
var typeList = assemblies.SelectMany(x => x.GetTypes()).Where(x => x.IsDefined(typeof(SelectResultAttribute), false));
foreach (var typeInfoItem in typeList)
{

View File

@ -1,5 +1,4 @@
using JiShe.IoT.BusinessSystemAggregation.Dto;
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.ServicePro;
using JiShe.ServicePro.ApacheIoTDB.Provider.Options;
using JiShe.ServicePro.Core;
@ -7,70 +6,34 @@ using JiShe.ServicePro.DataChannelManages;
using JiShe.ServicePro.DeviceManagement.DeviceInfos;
using JiShe.ServicePro.DeviceManagement.DeviceInfos.Dto;
using JiShe.ServicePro.DeviceManagement.Permissions;
using JiShe.ServicePro.DeviceManagement.ThingModels;
using JiShe.ServicePro.Dto;
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.FileManagement.Files;
using JiShe.ServicePro.FreeRedisProvider;
using JiShe.ServicePro.IoTDBManagement.DataChannels;
using JiShe.ServicePro.IoTDBManagement.TreeModels;
using JiShe.ServicePro.IoTDBManagement.TableModels;
using JiShe.ServicePro.OneNETManagement.OneNETDevices;
using JiShe.ServicePro.OneNETManagement.OneNETProducts;
using Mapster;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Content;
namespace JiShe.IoT.DeviceAggregation
{
/// <summary>
/// 设备聚合服务
/// </summary>
/// <param name="_logger"></param>
/// <param name="_deviceAppService">设备服务</param>
/// <param name="_oneNETDeviceService">OneNET设备服务</param>
/// <param name="_redisPubSubService">Redis发布订阅服务</param>
/// <param name="_ioTDBDataChannelManageService">数据通道</param>
/// <param name="logger"></param>
/// <param name="deviceAppService">设备服务</param>
/// <param name="oneNETDeviceService">OneNET设备服务</param>
/// <param name="redisPubSubService">Redis发布订阅服务</param>
/// <param name="ioTDBDataChannelManageService">数据通道</param>
/// <param name="_ioTDBOptions">IoTDBOptions</param>
/// <param name="_oneNETProductService">OneNET产品服务</param>
/// <param name="_deviceThingModelService">设备端物模型服务</param>
/// <param name="_platformThingModelInfoAppService">平台端端物模型服务</param>
/// <param name="_deviceFirmwareInfoService">设备固件服务</param>
/// <param name="_deviceUpgradeRecordService">设备升级记录服务</param>
/// <param name="_fileAppService">文件管理服务</param>
/// <param name="_serverOptions">应用服务配置</param>
/// <param name="_treeModelService">IoTDB树模型服务</param>
public class DeviceAggregationService(
ILogger<DeviceAggregationService> _logger,
IDeviceAppService _deviceAppService,
IOneNETDeviceService _oneNETDeviceService,
IReliableRedisPubSubService _redisPubSubService,
IIoTDBDataChannelManageService _ioTDBDataChannelManageService,
IOptions<IoTDBOptions> _ioTDBOptions,
IOptions<ServerApplicationOptions> _serverOptions,
IOneNETProductService _oneNETProductService,
IDeviceThingModelManagementAppService _deviceThingModelService,
IIoTPlatformThingModelInfoAppService _platformThingModelInfoAppService,
IDeviceFirmwareInfoService _deviceFirmwareInfoService,
IDeviceUpgradeRecordService _deviceUpgradeRecordService,
IFileAppService _fileAppService,
ITreeModelService _treeModelService) :
IoTDeviceBasicAppService(
_logger,
_deviceAppService,
_oneNETDeviceService,
_redisPubSubService,
_ioTDBDataChannelManageService,
_ioTDBOptions,
_serverOptions,
_oneNETProductService,
_deviceThingModelService,
_platformThingModelInfoAppService,
_deviceUpgradeRecordService,
_treeModelService),
IDeviceAggregationService
public class DeviceAggregationService(ILogger<DeviceAggregationService> logger, IDeviceAppService deviceAppService, IOneNETDeviceService oneNETDeviceService, IReliableRedisPubSubService redisPubSubService, IIoTDBDataChannelManageService ioTDBDataChannelManageService,IOptions<IoTDBOptions> _ioTDBOptions) : IoTAppService, IDeviceAggregationService
{
IoTDBOptions ioTDBOptions = _ioTDBOptions.Value;
/// <summary>
/// 管理后台创建设备信息
/// </summary>
@ -81,7 +44,7 @@ namespace JiShe.IoT.DeviceAggregation
{
try
{
input.DeviceSourceType = ServicePro.Enums.DeviceSourceTypeEnum.AdminSystem;
input.DeviceSourceTypeEnum = ServicePro.Enums.DeviceSourceTypeEnum.AdminSystem;
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
return await CTWingDeviceCreateAsync(input);
@ -122,7 +85,7 @@ namespace JiShe.IoT.DeviceAggregation
throw new UserFriendlyException($"批量创建设备失败设备信息不能超过100个。");
}
input.DeviceSourceType = ServicePro.Enums.DeviceSourceTypeEnum.AdminSystem;
input.DeviceSourceTypeEnum = ServicePro.Enums.DeviceSourceTypeEnum.AdminSystem;
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
@ -151,7 +114,7 @@ namespace JiShe.IoT.DeviceAggregation
{
try
{
input.DeviceSourceType = DeviceSourceTypeEnum.Workshop;
input.DeviceSourceTypeEnum = DeviceSourceTypeEnum.Workshop;
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
@ -191,7 +154,7 @@ namespace JiShe.IoT.DeviceAggregation
throw new UserFriendlyException($"批量创建设备失败设备信息不能超过100个。");
}
input.DeviceSourceType = DeviceSourceTypeEnum.Workshop;
input.DeviceSourceTypeEnum = DeviceSourceTypeEnum.Workshop;
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
@ -285,6 +248,37 @@ namespace JiShe.IoT.DeviceAggregation
}
}
/// <summary>
/// 更新设备信息并处理缓存
/// </summary>
/// <param name="input"></param>
/// <param name="pushResult"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
private async Task<DeviceManagementInfoDto> DeviceUpdateHandler(DeviceManagementInfoDto input, HttpDataResult pushResult)
{
UpdateDeviceInput updateDeviceInput = input.Adapt<UpdateDeviceInput>();
updateDeviceInput.IoTPlatformResponse = pushResult.Serialize();
updateDeviceInput.IsPlatformPushSuccess = true;
var updateResult = await deviceAppService.UpdateAsync(updateDeviceInput);
if (updateResult == null)
{
logger.LogError($"{nameof(CreateDeviceForApiAsync)} 更新设备信息失败:{input.Serialize()}");
throw new UserFriendlyException($"推送结果更新失败。");
}
//设备数据缓存到Redis
DeviceCacheInfos deviceCacheInfos = input.Adapt<DeviceCacheInfos>();
deviceCacheInfos.PlatformPassword = null;
FreeRedisProvider.Instance.HSet<DeviceCacheInfos>(RedisConst.CacheAllDeviceInfoHashKey, input.DeviceAddress, deviceCacheInfos);
return input.Adapt<DeviceManagementInfoDto>();
}
/// <summary>
/// 发送设备指令信息
/// </summary>
@ -294,341 +288,232 @@ namespace JiShe.IoT.DeviceAggregation
{
try
{
if (input.CommandContent == null || input.CommandContent.Keys.Count <= 0)
{
throw new UserFriendlyException($"指令参数异常");
}
var deviceInfo = await deviceAppService.FindByIdAsync(input);
if (deviceInfo == null)
{
throw new UserFriendlyException($"设备不存在");
}
//将指令存储
var receiveCommandInfoDto = new ReceiveCommandInfoDto()
var commandRequest = new OpenApiRequest()
{
Message = new ReceiveCommandInfoDto()
{
DeviceAddress = deviceInfo.DeviceAddress,
Commands = input.CommandContent,
DeviceType = deviceInfo.DeviceType,
Commands = input.CommandContent.Deserialize<Dictionary<string,object>>(),
DeviceType = input.DeviceType ?? DeviceTypeEnum.GATEWAY,//todo 设备类型 需要跟设备统一什么情况下知道具体设备类型
SourceType = DeviceTelemetrySourceTypeEnum.AdminSystem,
TelemetryType = input.TelemetryType ?? DeviceTelemetryCommandTypeEnum.,
IoTPlatform = deviceInfo.IoTPlatform,
TelemetryCommandType = DeviceTelemetryCommandTypeEnum.RoutineOperationSet,
}.Serialize(),
};
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, commandRequest, deviceInfo.Adapt<DeviceCacheInfos>(), commandRequest.Message);
//数据写入遥测任务数据存储通道
if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.OneNET)
{
return await DeviceCommandInfoToOneNET(deviceInfo, receiveCommandInfoDto);
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo));
return await DeviceCommandInfoToOneNET(deviceInfo, packetTaskInfo);
}
else if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.CTWing)
{
//await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.CTWingAepCommandIssuedEventName,commandRequest);
//return true;
throw new UserFriendlyException($"发送设备指令信息失败CTWing暂未实现。");
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.CTWingAepCommandIssuedEventName,commandRequest);
return true;
}
else
{
throw new UserFriendlyException($"发送设备指令信息失败,未找到对应的产品配置信息。");
throw new UserFriendlyException($"创建设备失败,未找到对应的产品配置信息。");
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 验证并获取固件信息和文件信息
/// </summary>
/// <param name="firmwareVersionDataId">固件版本数据Id</param>
/// <param name="iotPlatformProductId">物联网平台产品Id</param>
/// <param name="firmwareNotFoundErrorCode">固件信息不存在的错误代码(可选,用于批量操作时的错误代码)</param>
/// <param name="fileNotFoundErrorCode">文件信息不存在的错误代码(可选,用于批量操作时的错误代码)</param>
/// <returns>返回固件信息和文件信息的元组</returns>
private async Task<(DeviceFirmwareInfoDto FirmwareInfo, FileObjectDto FileInfo)> ValidateAndGetFirmwareInfoAsync(Guid firmwareVersionDataId, string iotPlatformProductId, string firmwareNotFoundErrorCode = null, string fileNotFoundErrorCode = null)
{
var deviceFirmwareVersionInfo = await _deviceFirmwareInfoService.FindByIdAsync(new IdInput() { Id = firmwareVersionDataId });
if (deviceFirmwareVersionInfo == null)
{
var errorMsg = $"产品Id{iotPlatformProductId}的新固件信息{firmwareVersionDataId}不存在";
throw new UserFriendlyException(errorMsg, firmwareNotFoundErrorCode ?? string.Empty);
}
var fileInfo = await _fileAppService.GetFileAsync(new IdInput() { Id = deviceFirmwareVersionInfo.FirmwareFileId });
if (fileInfo == null)
{
var errorMsg = $"产品Id{iotPlatformProductId}的新固件信息{deviceFirmwareVersionInfo.FirmwareFileName}固件文件不存在";
throw new UserFriendlyException(errorMsg, fileNotFoundErrorCode ?? string.Empty);
}
return (deviceFirmwareVersionInfo, fileInfo);
}
/// <summary>
/// 创建接收指令信息Dto
/// </summary>
/// <param name="deviceInfo">设备信息</param>
/// <returns>接收指令信息Dto</returns>
private ReceiveCommandInfoDto CreateReceiveCommandInfoDto(DeviceManagementInfoDto deviceInfo)
{
return new ReceiveCommandInfoDto()
{
DeviceAddress = deviceInfo.DeviceAddress,
DeviceType = deviceInfo.DeviceType,
SourceType = DeviceTelemetrySourceTypeEnum.AdminSystem,
IoTPlatform = deviceInfo.IoTPlatform,
};
}
/// <summary>
/// 发送设备升级指令信息
/// 业务系统批量创建设备信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<bool> DeviceUpgradeForApiAsync(DeviceUpgradeForApiInput input)
{
try
{
var deviceInfo = await deviceAppService.FindByIdAsync(input);
if (deviceInfo == null)
{
throw new UserFriendlyException($"设备{input.Id}不存在");
}
var receiveCommandInfoDto = CreateReceiveCommandInfoDto(deviceInfo);
var (deviceFirmwareVersionInfo, fileInfo) = await ValidateAndGetFirmwareInfoAsync(input.NowFirmwareVersionDataId, input.IoTPlatformProductId);
return await ProcessDeviceUpgradeByPlatformAsync(deviceInfo, receiveCommandInfoDto, deviceFirmwareVersionInfo, fileInfo, input);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 批量发送设备升级指令信息,只对同一个平台下,同一个产品下的设备进行固件升级信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<List<string>> DeviceBatchUpgradeForApiAsync(DeviceBatchUpgradeForApiInput input)
public async Task<bool> BatchCreateDeviceBusinessSystemAsync(BatchCreateDeviceAggregationInput input)
{
try
{
if (input.AddressList == null || input.AddressList.Count <= 0)
{
throw new UserFriendlyException($"{nameof(DeviceBatchUpgradeForApiAsync)} 设备批量升级时地址列表不能为空", "-101");
throw new UserFriendlyException($"业务系统批量创建设备信息,设备信息不能为空。");
}
if (input.AddressList.Count > 200)
if (input.AddressList.Count > 100)
{
throw new UserFriendlyException($"{nameof(DeviceBatchUpgradeForApiAsync)} 设备批量升级时地址列表数量不能超过200", "-102");
throw new UserFriendlyException($"业务系统批量创建设备信息设备信息不能超过100个。");
}
var (deviceFirmwareVersionInfo, fileInfo) = await ValidateAndGetFirmwareInfoAsync(input.NowFirmwareVersionDataId, input.IoTPlatformProductId, "-103", "-104");
var deviceInfoList = await deviceAppService.FindByDeviceAddressAsync(new FindByDeviceAddressInput() { AddressList = input.AddressList, IoTPlatform = input.IoTPlatform, IoTPlatformProductId = input.IoTPlatformProductId });
//如果是OneNET平台提前获取产品信息和物模型信息避免在循环中重复调用
dynamic oneNETProductInfo = null;
dynamic oneNETPlatformThingModelInfo = null;
if (input.IoTPlatform == IoTPlatformTypeEnum.OneNET && deviceInfoList != null && deviceInfoList.Count > 0)
if (input.DeviceSourceTypeEnum != DeviceSourceTypeEnum.Prepay && input.DeviceSourceTypeEnum != DeviceSourceTypeEnum.Energy)
{
var firstDevice = deviceInfoList.FirstOrDefault();
if (firstDevice != null)
throw new UserFriendlyException($"业务系统批量创建设备信息,设备来源异常。");
}
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
var (productInfo, platformThingModelInfo) = await GetOneNETProductAndThingModelInfoAsync(firstDevice.IoTPlatformProductId, firstDevice.DeviceAddress);
oneNETProductInfo = productInfo;
oneNETPlatformThingModelInfo = platformThingModelInfo;
return await CTWingDeviceBatchCreateAsync(input);
}
else if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.OneNET)
{
return await OneNETDeviceBatchCreateAsync(input);
}
throw new UserFriendlyException($"不支持的物联网平台");
}
catch (Exception)
{
throw;
}
}
List<string> deviceAddress = new List<string>();//异常的设备地址集合
#region OneNET
/// <summary>
/// OneNET设备创建
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected async Task<bool> OneNETDeviceCreateAsync(CreateDeviceAggregationInput input)
{
try
{
CreateDeviceInput createDeviceInput = input.Adapt<CreateDeviceInput>();
var productInfo = await FreeSqlDbContext.Instance.Select<OneNETProductInfos>()
.Where(e => e.IoTPlatformProductId == input.IoTPlatformProductId)//此处不需要过滤产品状态,方便测试产品配置信息是否准确,避免跟车间生产搞混
.WhereIf(input.DeviceSourceTypeEnum == DeviceSourceTypeEnum.Workshop, e => e.IsEnabled == true)
.FirstAsync();
if (productInfo == null)
{
throw new UserFriendlyException($"创建设备失败,未找到对应的产品配置信息。");
}
createDeviceInput.DeviceName = input.DeviceAddress;
createDeviceInput.IoTPlatformAccountId = productInfo.OneNETAccountId;
createDeviceInput.IoTPlatformDeviceOpenInfo = $"{input.IoTPlatformProductId}{input.DeviceAddress}";
createDeviceInput.PlatformPassword = productInfo.ProductAccesskey;
createDeviceInput.IoTPlatformProductName = productInfo.ProductName;
createDeviceInput.AccountPhoneNumber = productInfo.AccountPhoneNumber;
var insertResult = await deviceAppService.CreateAsync(createDeviceInput);
if (insertResult == null)
{
logger.LogError($"{nameof(CreateDeviceForApiAsync)} 添加设备信息失败:{input.Serialize()}");
return false;
}
//推送至OneNET平台
var pushResult = await oneNETDeviceService.CreateDeviceInfoAsync(new CreateDeviceInfoInput()
{
DeviceName = createDeviceInput.IoTPlatformDeviceOpenInfo,
ProductId = productInfo.IoTPlatformProductId,
OneNETAccountId = productInfo.OneNETAccountId,
Description = input.DeviceAddress,
});
if (pushResult == null || pushResult.Code != ServicePro.Enums.ResponeResultEnum.Success)
{
logger.LogError($"{nameof(CreateDeviceForApiAsync)} 推送设备信息失败:{pushResult.Serialize()}");
return false;
}
await DeviceUpdateHandler(insertResult, pushResult);
return true;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// OneNET设备批量创建
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected async Task<bool> OneNETDeviceBatchCreateAsync(BatchCreateDeviceAggregationInput input)
{
try
{
var productInfo = await FreeSqlDbContext.Instance.Select<OneNETProductInfos>()
.Where(e => e.IoTPlatformProductId == input.IoTPlatformProductId)//此处不需要过滤产品状态,方便测试产品配置信息是否准确,避免跟车间生产搞混
.WhereIf(input.DeviceSourceTypeEnum == DeviceSourceTypeEnum.Workshop, e => e.IsEnabled == true)
.FirstAsync();
if (productInfo == null)
{
throw new UserFriendlyException($"批量创建设备失败,未找到对应的产品配置信息。");
}
BatchCreateDeviceInput batchCreateDeviceInput = new BatchCreateDeviceInput()
{
IoTPlatform = input.IoTPlatform,
AddressList = input.AddressList,
DeviceInputs = new List<CreateDeviceInput>()
};
foreach (var item in input.AddressList)
{
var deviceInfo = deviceInfoList.Where(d => d.DeviceAddress == item).FirstOrDefault();
if (deviceInfo == null)
{
deviceAddress.Add(item);
continue;
}
CreateDeviceInput createDeviceInput = input.Adapt<CreateDeviceInput>();
createDeviceInput.DeviceName = item;
createDeviceInput.DeviceAddress = item;
createDeviceInput.IoTPlatformAccountId = productInfo.OneNETAccountId;
createDeviceInput.IoTPlatformDeviceOpenInfo = $"{input.IoTPlatformProductId}{item}";
createDeviceInput.PlatformPassword = productInfo.ProductAccesskey;
createDeviceInput.IoTPlatformProductName = productInfo.ProductName;
createDeviceInput.AccountPhoneNumber = productInfo.AccountPhoneNumber;
createDeviceInput.DeviceSourceTypeEnum = input.DeviceSourceTypeEnum.Value;
var receiveCommandInfoDto = CreateReceiveCommandInfoDto(deviceInfo);
var deviceUpgradeRecordInput = input.Adapt<DeviceUpgradeForApiInput>();
deviceUpgradeRecordInput.Id = deviceInfo.Id;
try
{
var oneNETHandleResult = await ProcessDeviceUpgradeByPlatformAsync(deviceInfo, receiveCommandInfoDto, deviceFirmwareVersionInfo, fileInfo, deviceUpgradeRecordInput, oneNETProductInfo, oneNETPlatformThingModelInfo);
if (!oneNETHandleResult)
{
deviceAddress.Add(item);
}
}
catch (Exception)
{
deviceAddress.Add(item);
}
}
return deviceAddress;
}
catch (Exception)
{
throw;
}
batchCreateDeviceInput.DeviceInputs.Add(createDeviceInput);
}
/// <summary>
/// 下载设备固件文件
/// </summary>
[AllowAnonymous]
public async Task<RemoteStreamContent> DownloadFirmwareInfoAsync(IdInput input)
var insertResult = await deviceAppService.BatchCreateAsync(batchCreateDeviceInput);
if (insertResult == null)
{
try
{
// Lua 脚本获取并删除键原子操作升级记录Id与文件ID关系缓存
string cacheKey = string.Format($"{RedisConst.CacheDeviceUpgradeRecordDataKey}", input.Id);
var redisResult = await FreeRedisProvider.Instance.GetAsync(cacheKey);
if (string.IsNullOrWhiteSpace(redisResult))
{
throw new UserFriendlyException($"{nameof(DownloadFirmwareInfoAsync)} 设备升级记录信息 {input.Id} 不存在");
logger.LogError($"{nameof(OneNETDeviceBatchCreateAsync)} OneNET设备批量创建添加设备信息失败{input.Serialize()}");
return false;
}
_logger.LogWarning($"{nameof(DownloadFirmwareInfoAsync)} 设备升级下载固件,记录信息 {input.Id} 缓存结果:{redisResult}");
//将缓存中的文件ID转换成GUID
Guid fileId = new Guid(redisResult);
var downLoadResult = await _fileAppService.AllowDownloadAsync(new IdInput()
//推送至OneNET平台
var oneNETBatchCreateDeviceInfoInput = new BatchCreateDeviceInfoInput()
{
Id = fileId,
});
if (downLoadResult.ContentLength.HasValue && downLoadResult.ContentLength.Value > 0)
{
//更新状态为文件下载中
await deviceUpgradeRecordService.UpdateStatusAsync(new UpdateStatusInput()
{
Id = input.Id,
UpgradeStatus = DeviceUpgradeStatusTypeEnum.Downloading,
});
}
return downLoadResult;
}
catch (Exception ex)
{
_logger.LogError($"{nameof(DownloadFirmwareInfoAsync)}{input.Id}下载设备固件文件发生异常,{ex.Message}");
throw;
}
}
/// <summary>
/// 获取设备属性最新值
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<Dictionary<string, object>> GetDevicePropertyValueForApiAsync(DevicePropertyValueForApiInput input)
{
try
{
if (input.PropertyList == null || input.PropertyList.Count <= 0)
{
throw new UserFriendlyException($"属性标识符参数异常");
}
var deviceInfo = await deviceAppService.FindByIdAsync(input);
if (deviceInfo == null)
{
throw new UserFriendlyException($"设备不存在");
}
//数据写入遥测任务数据存储通道
var commandRequest = new OpenApiRequest()
{
Message = input.Serialize(),
ProductId = productInfo.IoTPlatformProductId,
OneNETAccountId = productInfo.OneNETAccountId,
DeviceList = new List<string>()
};
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, commandRequest, deviceInfo.Adapt<DeviceCacheInfos>(), input.PropertyList.Serialize());
packetTaskInfo.TelemetryType = (int)DeviceTelemetryCommandTypeEnum.GetAttributeData;
oneNETBatchCreateDeviceInfoInput.DeviceList = batchCreateDeviceInput.DeviceInputs.Select(d => d.IoTPlatformDeviceOpenInfo).ToList();
if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.OneNET)
{
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETUpgradeCommandIssuedEventName, packetTaskInfo));
var pushResult = await oneNETDeviceService.BatchCreateDeviceInfoAsync(oneNETBatchCreateDeviceInfoInput);
return await DevicePropertyValueToOneNET(deviceInfo, input);
}
else if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.CTWing)
if (pushResult == null || pushResult.Code != ServicePro.Enums.ResponeResultEnum.Success)
{
//await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.CTWingAepCommandIssuedEventName,commandRequest);
//return true;
throw new UserFriendlyException($"发送设备指令信息失败CTWing暂未实现。");
}
else
{
throw new UserFriendlyException($"发送设备指令信息失败,未找到对应的产品配置信息。");
}
}
catch (Exception)
{
throw;
}
logger.LogError($"{nameof(CreateDeviceForApiAsync)} 推送设备信息失败:{pushResult.Serialize()}");
return false;
}
/// <summary>
/// 绑定设备端物模型
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<bool> BindingDeviceThingModel(BindingDeviceThingModelInput input)
foreach (var item in insertResult)
{
try
var successEntity = pushResult.Data.Successlist.Where(d => d.DeviceName == item.IoTPlatformDeviceOpenInfo).FirstOrDefault();
if (successEntity != null)
{
List<DeviceManagementInfoDto> bindingDeviceList = await deviceAppService.BindingDeviceThingModel(input);
if (bindingDeviceList == null)
{
throw new UserFriendlyException($"绑定设备端物模型失败", "-101");
}
foreach (var item in bindingDeviceList)
{
if (item.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
_logger.LogInformation($"{nameof(BindingDeviceThingModel)} CTWing设备{item.DeviceAddress}绑定设备物模型未实现");
continue;
}
else if (item.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.OneNET)
{
var platformThingModelInfos = await platformThingModelInfoAppService.FindByPlatformProductIdAsync(new IdInput<string>() { Id = item.IoTPlatformProductId });
return await DeviceCommandInfoToOneNET(item, new ReceiveCommandInfoDto()
{
DeviceType = item.DeviceType,
SourceType = DeviceTelemetrySourceTypeEnum.AdminSystem,
DeviceAddress = item.DeviceAddress,
IoTPlatform = item.IoTPlatform,
Commands = new Dictionary<string, object>() {
{ThingModelFixedTypeConst.SpecialCommand,
""}
},
TelemetryCommandType = DeviceTelemetryCommandTypeEnum.ThingModelSwitch,
}, platformThingModelInfos);
await DeviceUpdateHandler(item, HttpDataResultExtensions.Success(successEntity));
}
}
return true;
}
catch (Exception)
{
@ -638,6 +523,153 @@ namespace JiShe.IoT.DeviceAggregation
}
/// <summary>
/// 重新推送设备信息到OneNET物联网平台
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task<DeviceManagementInfoDto> RepushDeviceInfoToOneNET(DeviceManagementInfoDto input)
{
try
{
var productInfo = await FreeSqlDbContext.Instance.Select<OneNETProductInfos>()
.Where(e => e.IoTPlatformProductId == input.IoTPlatformProductId)
.FirstAsync();
if (productInfo == null)
{
throw new UserFriendlyException($"推送失败,未找到对应的产品配置信息。");
}
//检查OneNET平台设备是否已经存在
var oneNETDeviceInfoResult = await oneNETDeviceService.DeviceInfoDetailAsync(new DeviceInfoDetailInput()
{
DeviceName = input.IoTPlatformDeviceOpenInfo,
ProductId = productInfo.IoTPlatformProductId,
OneNETAccountId = productInfo.OneNETAccountId,
});
if (oneNETDeviceInfoResult != null && oneNETDeviceInfoResult.Code == ServicePro.Enums.ResponeResultEnum.Success)
{
throw new UserFriendlyException($"推送失败OneNET账号{productInfo.AccountPhoneNumber}的产品下{productInfo.ProductName}已经存在该设备{input.DeviceAddress}。");
}
//推送至OneNET平台
var pushResult = await oneNETDeviceService.CreateDeviceInfoAsync(new CreateDeviceInfoInput()
{
DeviceName = input.IoTPlatformDeviceOpenInfo,
ProductId = productInfo.IoTPlatformProductId,
OneNETAccountId = productInfo.OneNETAccountId,
Description = input.DeviceAddress,
});
if (pushResult == null || pushResult.Code != ServicePro.Enums.ResponeResultEnum.Success)
{
logger.LogError($"{nameof(CreateDeviceForApiAsync)} 推送设备信息失败:{pushResult.Serialize()}");
throw new UserFriendlyException($"平台请求失败。");
}
return await DeviceUpdateHandler(input, pushResult);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 删除OneNET平台设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task<bool> DeleteDeviceInfoToOneNET(DeviceManagementInfoDto input)
{
try
{
var productInfo = await FreeSqlDbContext.Instance.Select<OneNETProductInfos>()
.Where(e => e.IoTPlatformProductId == input.IoTPlatformProductId)
.FirstAsync();
if (productInfo == null)
{
throw new UserFriendlyException($"删除失败,未找到对应的产品配置信息。");
}
//删除OneNET平台设备信息
var deleteResult = await oneNETDeviceService.DeleteDeviceInfoAsync(new DeleteDeviceInfoInput()
{
DeviceName = input.IoTPlatformDeviceOpenInfo,
ProductId = productInfo.IoTPlatformProductId,
OneNETAccountId = productInfo.OneNETAccountId,
});
if (deleteResult == null || deleteResult.Code != ServicePro.Enums.ResponeResultEnum.Success)
{
logger.LogError($"{nameof(CreateDeviceForApiAsync)} 删除设备信息失败:{deleteResult.Serialize()}");
throw new UserFriendlyException($"删除设备平台请求失败。");
}
var localDeleteReult = await deviceAppService.DeleteAsync(new IdInput() { Id = input.Id });
if (localDeleteReult == false)
{
throw new UserFriendlyException($"平台设备信息删除成功,系统删除设备失败。");
}
return localDeleteReult;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 发送OneNET平台设备指令
/// </summary>
/// <param name="deviceInfo"></param>
/// <param name="packetTaskInfo"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task<bool> DeviceCommandInfoToOneNET(DeviceManagementInfoDto deviceInfo, DeviceTelemetryPacketTaskInfo packetTaskInfo)
{
try
{
//检查下设备是否在线
var deviceOnlineStatus = await oneNETDeviceService.DeviceInfoDetailAsync(new DeviceInfoDetailInput()
{
DeviceName = deviceInfo.IoTPlatformDeviceOpenInfo,
OneNETAccountId = deviceInfo.IoTPlatformAccountId,
ProductId = deviceInfo.IoTPlatformProductId,
});
if (deviceOnlineStatus == null || deviceOnlineStatus.Code != ResponeResultEnum.Success)
{
throw new UserFriendlyException("获取平台设备信息失败");
}
if (deviceOnlineStatus.Data.Status != 1)
{
throw new UserFriendlyException("设备不在线");
}
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo);
return true;
}
catch (Exception)
{
throw;
}
}
#endregion
#region CTWing
/// <summary>
/// CTWing 设备创建
@ -649,6 +681,25 @@ namespace JiShe.IoT.DeviceAggregation
throw new UserFriendlyException($"CTWing 设备创建失败,功能未实现。");
}
/// <summary>
/// CTWing 批量设备创建
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected async Task<bool> CTWingDeviceBatchCreateAsync(BatchCreateDeviceAggregationInput input)
{
try
{
throw new UserFriendlyException($"CTWing 批量设备创建失败CTWing暂未实现。");
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 重新推送设备信息到CTWing物联网平台

View File

@ -12,6 +12,8 @@ global using JiShe.IoT.Permissions;
global using Microsoft.AspNetCore.Authorization;
global using Microsoft.Extensions.Configuration;
global using Volo.Abp.Application.Services;
global using Volo.Abp.AutoMapper;
global using Volo.Abp.BackgroundJobs.Hangfire;
global using Volo.Abp.DependencyInjection;
global using Volo.Abp.Modularity;
global using Profile = AutoMapper.Profile;

View File

@ -1,5 +1,6 @@
using JiShe.ServicePro;
using JiShe.ServicePro.ApacheIoTDB.Provider.Options;
using JiShe.ServicePro.Consts;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.Dto;
using JiShe.ServicePro.Encrypt;
@ -8,6 +9,7 @@ using JiShe.ServicePro.FreeRedisProvider;
using JiShe.ServicePro.FreeSqlProvider;
using JiShe.ServicePro.IoTDBManagement.TableModels;
using Microsoft.Extensions.Logging;
using System.Xml.Linq;
using Volo.Abp;
namespace JiShe.IoT
@ -47,22 +49,22 @@ namespace JiShe.IoT
var oneNETIssueMessageEntity = new DeviceTelemetryPacketTaskInfo()
{
DataBaseName = iotDBOptions.DataBaseName,
DeviceType = $"{deviceInfo.DeviceType}",
DeviceAddress = deviceInfo.DeviceAddress,
DeviceType = $"{commandIssueInfo.DeviceType}",
DeviceAddress = commandIssueInfo.DeviceAddress,
IssueRawMessage = input.Serialize(),
IssuePayload = messageBody,
IoTDataType = IoTDBDataTypeConst.Command,
TelemetryType = (int)commandIssueInfo.TelemetryType,
TelemetrySource = (int)commandIssueInfo.SourceType,
IoTPlatform = (int)deviceInfo.IoTPlatform,
IoTPlatform = (int)commandIssueInfo.IoTPlatform,
IoTPlatformProductId = deviceInfo.IoTPlatformProductId,
IoTPlatformDeviceOpenInfo = deviceInfo.IoTPlatformDeviceOpenInfo,
IoTPlatformAccountId = deviceInfo.IoTPlatformAccountId,
AccountPhoneNumber = deviceInfo.AccountPhoneNumber,
IoTPlatformProductName = deviceInfo.IoTPlatformProductName,
IssuePayload = messageBody,
RetryCount = 0,
IssueStatus = (int)DeviceCommandIssueStatusEnum.Unprocessed,
LastIssueTime = DateTime.Now,
TelemetryType = (int)commandIssueInfo.TelemetryCommandType,
LastIssueTime = DateTime.Now
};
return oneNETIssueMessageEntity;

View File

@ -0,0 +1,10 @@
namespace JiShe.IoT
{
public class IoTApplicationAutoMapperProfile : Profile
{
public IoTApplicationAutoMapperProfile()
{
}
}
}

View File

@ -35,6 +35,10 @@ namespace JiShe.IoT
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<IoTApplicationModule>();
});
}
@ -57,7 +61,6 @@ namespace JiShe.IoT
var serviceCommunicationChannelSubscriberService = context.ServiceProvider.GetRequiredService<IServiceCommunicationChannelSubscriberService>();
serviceCommunicationChannelSubscriberService.ServiceCommunicationDeviceStatusSubscriber();
serviceCommunicationChannelSubscriberService.ServiceCommunicationDeviceUpgradeSubscriber();
}
}
}

View File

@ -1,929 +0,0 @@
using JiShe.IoT.DeviceAggregation;
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.ServicePro;
using JiShe.ServicePro.ApacheIoTDB.Provider.Options;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.DataChannelManages;
using JiShe.ServicePro.DeviceManagement.DeviceInfos;
using JiShe.ServicePro.DeviceManagement.DeviceInfos.Dto;
using JiShe.ServicePro.DeviceManagement.ThingModels;
using JiShe.ServicePro.Dto;
using JiShe.ServicePro.Encrypt;
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.FileManagement.Files;
using JiShe.ServicePro.FreeRedisProvider;
using JiShe.ServicePro.FreeSqlProvider;
using JiShe.ServicePro.IoTDBManagement.DataChannels;
using JiShe.ServicePro.IoTDBManagement.TableModels;
using JiShe.ServicePro.IoTDBManagement.TreeModels;
using JiShe.ServicePro.OneNET.Provider.OpenApiModels.Commands;
using JiShe.ServicePro.OneNETManagement.OneNETDevices;
using JiShe.ServicePro.OneNETManagement.OneNETProducts;
using Mapster;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using Volo.Abp;
namespace JiShe.IoT
{
/// <summary>
/// 设备基础服务
/// </summary>
public abstract class IoTDeviceBasicAppService : IoTAppService
{
protected readonly ILogger<IoTDeviceBasicAppService> logger;
protected readonly IDeviceAppService deviceAppService;
protected readonly IOneNETDeviceService oneNETDeviceService;
protected readonly IReliableRedisPubSubService redisPubSubService;
protected readonly IIoTDBDataChannelManageService ioTDBDataChannelManageService;
protected readonly IoTDBOptions ioTDBOptions;
protected readonly ServerApplicationOptions serverApplicationOptions;
protected readonly IOneNETProductService oneNETProductService;
protected readonly IDeviceThingModelManagementAppService deviceThingModelService;
protected readonly IIoTPlatformThingModelInfoAppService platformThingModelInfoAppService;
protected readonly IDeviceUpgradeRecordService deviceUpgradeRecordService;
protected readonly ITreeModelService treeModelService;
//需要过滤的命令
protected readonly List<string> exitCommands = new List<string>()
{
ThingModelFixedTypeConst.FIRMWARE_UPGRADE.ToLowerInvariant(),
ThingModelFixedTypeConst.SpecialCommand.ToLowerInvariant(),
ThingModelFixedTypeConst.DEVICE_TO_MASTER_SWITCH.ToLowerInvariant(),
};
/// <param name="logger"></param>
/// <param name="deviceAppService">设备服务</param>
/// <param name="oneNETDeviceService">OneNET设备服务</param>
/// <param name="redisPubSubService">Redis发布订阅服务</param>
/// <param name="ioTDBDataChannelManageService">数据通道</param>
/// <param name="_ioTDBOptions">IoTDBOptions</param>
/// <param name="oneNETProductService">OneNET产品服务</param>
/// <param name="deviceThingModelService">设备端物模型服务</param>
/// <param name="platformThingModelInfoAppService">平台端端物模型服务</param>
/// <param name="deviceUpgradeRecordService">设备升级记录服务</param>
/// <param name="_serverOptions">应用服务配置</param>
/// <param name="treeModelService">IoTDB树模型服务</param>
protected IoTDeviceBasicAppService(
ILogger<IoTDeviceBasicAppService> logger,
IDeviceAppService deviceAppService,
IOneNETDeviceService oneNETDeviceService,
IReliableRedisPubSubService redisPubSubService,
IIoTDBDataChannelManageService ioTDBDataChannelManageService,
IOptions<IoTDBOptions> _ioTDBOptions,
IOptions<ServerApplicationOptions> _serverOptions,
IOneNETProductService oneNETProductService,
IDeviceThingModelManagementAppService deviceThingModelService,
IIoTPlatformThingModelInfoAppService platformThingModelInfoAppService,
IDeviceUpgradeRecordService deviceUpgradeRecordService,
ITreeModelService treeModelService)
{
this.logger = logger;
this.deviceAppService = deviceAppService;
this.oneNETDeviceService = oneNETDeviceService;
this.redisPubSubService = redisPubSubService;
this.ioTDBDataChannelManageService = ioTDBDataChannelManageService;
this.ioTDBOptions = _ioTDBOptions.Value;
this.serverApplicationOptions = _serverOptions.Value;
this.oneNETProductService = oneNETProductService;
this.deviceThingModelService = deviceThingModelService;
this.platformThingModelInfoAppService = platformThingModelInfoAppService;
this.deviceUpgradeRecordService = deviceUpgradeRecordService;
this.treeModelService = treeModelService;
}
#region OneNET
/// <summary>
/// OneNET设备创建
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected async Task<bool> OneNETDeviceCreateAsync(CreateDeviceAggregationInput input)
{
try
{
CreateDeviceInput createDeviceInput = input.Adapt<CreateDeviceInput>();
var productInfo = await oneNETProductService.GetProductInfoAsync(new IdInput<string>() { Id = input.IoTPlatformProductId });
if (productInfo == null)
{
throw new UserFriendlyException($"OneNET创建设备失败未找到对应的产品配置信息。");
}
if (input.DeviceSourceType == DeviceSourceTypeEnum.Workshop && !productInfo.IsEnabled) //车间生产推送,必须是已经启用的产品才可以
{
throw new UserFriendlyException($"车间生产推送OneNET创建设备失败产品未启用。");
}
createDeviceInput.DeviceName = input.DeviceAddress;
createDeviceInput.IoTPlatformAccountId = productInfo.OneNETAccountId;
createDeviceInput.IoTPlatformDeviceOpenInfo = $"{input.IoTPlatformProductId}{input.DeviceAddress}";
createDeviceInput.PlatformPassword = productInfo.ProductAccesskey;
createDeviceInput.IoTPlatformProductName = productInfo.ProductName;
createDeviceInput.AccountPhoneNumber = productInfo.AccountPhoneNumber;
if (input.DeviceType.HasValue)
{
createDeviceInput.DeviceType = input.DeviceType.Value;
}
var insertResult = await deviceAppService.CreateAsync(createDeviceInput);
if (insertResult == null)
{
logger.LogError($"{nameof(OneNETDeviceCreateAsync)} 添加设备信息失败:{input.Serialize()}");
return false;
}
//推送至OneNET平台
var pushResult = await oneNETDeviceService.CreateDeviceInfoAsync(new CreateDeviceInfoInput()
{
DeviceName = createDeviceInput.IoTPlatformDeviceOpenInfo,
ProductId = productInfo.IoTPlatformProductId,
OneNETAccountId = productInfo.OneNETAccountId,
Description = input.DeviceAddress,
});
if (pushResult == null || pushResult.Code != ServicePro.Enums.ResponeResultEnum.Success || pushResult.Data == null)
{
logger.LogError($"{nameof(OneNETDeviceCreateAsync)} 推送设备信息失败:{pushResult.Serialize()}");
return false;
}
//更新OneNET平台推送结果
await DeviceUpdateHandler(insertResult, pushResult, pushResult.Data.SecurityKey);
return true;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// OneNET设备批量创建
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected async Task<bool> OneNETDeviceBatchCreateAsync(BatchCreateDeviceAggregationInput input)
{
try
{
var productInfo = await FreeSqlDbContext.Instance.Select<OneNETProductInfos>()
.Where(e => e.IoTPlatformProductId == input.IoTPlatformProductId)//此处不需要过滤产品状态,方便测试产品配置信息是否准确,避免跟车间生产搞混
.WhereIf(input.DeviceSourceType == DeviceSourceTypeEnum.Workshop, e => e.IsEnabled == true)
.FirstAsync();
if (productInfo == null)
{
throw new UserFriendlyException($"批量创建设备失败,未找到对应的产品配置信息。");
}
BatchCreateDeviceInput batchCreateDeviceInput = new BatchCreateDeviceInput()
{
IoTPlatform = input.IoTPlatform,
AddressList = input.AddressList,
DeviceInputs = new List<CreateDeviceInput>()
};
//检查网关或者直连设备信息是否已经存在
var checkDevicesInfos = await deviceAppService.FindByDeviceAddressAsync(new FindByDeviceAddressInput()
{
AddressList = input.AddressList,
IoTPlatform = input.IoTPlatform,
IoTPlatformProductId = input.IoTPlatformProductId,
});
foreach (var item in input.DeviceInfos)
{
if (checkDevicesInfos != null)
{
var checkDevices = checkDevicesInfos.Where(e => e.DeviceAddress == item.DeviceAddress).FirstOrDefault();
if (checkDevices != null && checkDevices.IsPlatformPushSuccess)
{
logger.LogError($"{nameof(OneNETDeviceBatchCreateAsync)} 平台{input.IoTPlatform} 产品 {input.IoTPlatformProductId} 下设备信息已存在:{item.DeviceAddress}");
continue;
}
}
CreateDeviceInput createDeviceInput = input.Adapt<CreateDeviceInput>();
createDeviceInput.DeviceName = item.DeviceAddress;
createDeviceInput.DeviceAddress = item.DeviceAddress;
createDeviceInput.IoTPlatformAccountId = productInfo.OneNETAccountId;
createDeviceInput.IoTPlatformDeviceOpenInfo = $"{input.IoTPlatformProductId}{item.DeviceAddress}";
createDeviceInput.PlatformPassword = productInfo.ProductAccesskey;
createDeviceInput.IoTPlatformProductName = productInfo.ProductName;
createDeviceInput.AccountPhoneNumber = productInfo.AccountPhoneNumber;
if (input.DeviceSourceType.HasValue)
{
createDeviceInput.DeviceSourceType = input.DeviceSourceType.Value;
}
if (input.DeviceType.HasValue)
{
createDeviceInput.DeviceType = input.DeviceType.Value;
}
if (item.BusinessSystemDeviceDataId.HasValue)
{
createDeviceInput.BusinessSystemDeviceDataId = item.BusinessSystemDeviceDataId.Value;
}
batchCreateDeviceInput.DeviceInputs.Add(createDeviceInput);
}
if (batchCreateDeviceInput.DeviceInputs != null || batchCreateDeviceInput.DeviceInputs.Count > 0)
{
var insertResult = await deviceAppService.BatchCreateAsync(batchCreateDeviceInput);
if (insertResult == null)
{
logger.LogError($"{nameof(OneNETDeviceBatchCreateAsync)} OneNET设备批量创建添加设备信息失败{input.Serialize()}");
return false;
}
//网关或者直连设备 推送至OneNET平台
var oneNETBatchCreateDeviceInfoInput = new BatchCreateDeviceInfoInput()
{
ProductId = productInfo.IoTPlatformProductId,
OneNETAccountId = productInfo.OneNETAccountId,
DeviceList = new List<string>()
};
oneNETBatchCreateDeviceInfoInput.DeviceList = batchCreateDeviceInput.DeviceInputs.Select(d => d.IoTPlatformDeviceOpenInfo).ToList();
var pushResult = await oneNETDeviceService.BatchCreateDeviceInfoAsync(oneNETBatchCreateDeviceInfoInput);
if (pushResult == null || pushResult.Code != ServicePro.Enums.ResponeResultEnum.Success)
{
logger.LogError($"{nameof(OneNETDeviceBatchCreateAsync)} 推送设备信息失败:{pushResult.Serialize()}");
return false;
}
//更新OneNET平台推送结果
foreach (var item in insertResult)
{
var successEntity = pushResult.Data.Successlist.Where(d => d.DeviceName == item.IoTPlatformDeviceOpenInfo).FirstOrDefault();
if (successEntity != null)
{
await DeviceUpdateHandler(item, HttpDataResultExtensions.Success(successEntity), successEntity.SecurityKey);
}
}
}
//暂不考虑子设备地址信息,由网关设备主动上报
return true;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 重新推送设备信息到OneNET物联网平台
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task<DeviceManagementInfoDto> RepushDeviceInfoToOneNET(DeviceManagementInfoDto input)
{
try
{
//检查OneNET平台设备是否已经存在
var oneNETDeviceInfoResult = await oneNETDeviceService.DeviceInfoDetailAsync(new DeviceInfoDetailInput()
{
DeviceName = input.IoTPlatformDeviceOpenInfo,
ProductId = input.IoTPlatformProductId,
OneNETAccountId = input.IoTPlatformAccountId,
});
if (oneNETDeviceInfoResult != null && oneNETDeviceInfoResult.Code == ServicePro.Enums.ResponeResultEnum.Success)
{
throw new UserFriendlyException($"OneNET推送失败账号产品下{input.IoTPlatformProductId}已经存在该设备{input.DeviceAddress}。");
}
//推送至OneNET平台
var pushResult = await oneNETDeviceService.CreateDeviceInfoAsync(new CreateDeviceInfoInput()
{
DeviceName = input.IoTPlatformDeviceOpenInfo,
ProductId = input.IoTPlatformProductId,
OneNETAccountId = input.IoTPlatformAccountId,
Description = input.DeviceAddress,
});
if (pushResult == null || pushResult.Code != ServicePro.Enums.ResponeResultEnum.Success || pushResult.Data == null)
{
logger.LogError($"{nameof(RepushDeviceInfoToOneNET)} 推送设备信息失败:{pushResult.Serialize()}");
throw new UserFriendlyException($"平台请求失败。");
}
return await DeviceUpdateHandler(input, pushResult, pushResult.Data.SecurityKey);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 删除OneNET平台设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task<bool> DeleteDeviceInfoToOneNET(DeviceManagementInfoDto input)
{
try
{
//删除OneNET平台设备信息
var deleteResult = await oneNETDeviceService.DeleteDeviceInfoAsync(new DeleteDeviceInfoInput()
{
DeviceName = input.IoTPlatformDeviceOpenInfo,
ProductId = input.IoTPlatformProductId,
OneNETAccountId = input.IoTPlatformAccountId,
});
if (deleteResult == null || deleteResult.Code != ServicePro.Enums.ResponeResultEnum.Success)
{
logger.LogError($"{nameof(DeleteDeviceInfoToOneNET)} 删除设备信息失败:{deleteResult.Serialize()}");
throw new UserFriendlyException($"删除设备平台请求失败。");
}
var localDeleteReult = await deviceAppService.DeleteAsync(new IdInput() { Id = input.Id });
if (localDeleteReult == false)
{
throw new UserFriendlyException($"平台设备信息删除成功,系统删除设备失败。");
}
return localDeleteReult;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 发送OneNET平台设备指令
/// </summary>
/// <param name="deviceInfo">设备信息</param>
/// <param name="input">指令信息</param>
/// <param name="platformThingModelInfos">平台端物模型信息</param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
protected async Task<bool> DeviceCommandInfoToOneNET(DeviceManagementInfoDto deviceInfo, ReceiveCommandInfoDto input, List<IoTPlatformThingModelInfoDto> platformThingModelInfos = null)
{
try
{
//获取设备对应的平台端物模型信息,校验前端传入的属性标识集合是否存在不合法的属性标识符
if (platformThingModelInfos == null || platformThingModelInfos.Count <= 0)
{
platformThingModelInfos = await platformThingModelInfoAppService.FindByPlatformProductIdAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
}
if (platformThingModelInfos == null || platformThingModelInfos.Count <= 0)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}的平台端物模型信息不存在。");
}
foreach (var item in input.Commands)
{
var tempPlatformThingModelInfo = platformThingModelInfos.Where(d => d.IoTPlatformRawFieldName == item.Key).FirstOrDefault();
if (tempPlatformThingModelInfo == null)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台端物模型信息不存在属性标识符{item.Key}。");
}
//排除升级指令
if (tempPlatformThingModelInfo.StandardFieldName.ToLowerInvariant() == ThingModelFixedTypeConst.FIRMWARE_UPGRADE.ToLowerInvariant())
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台端物模型属性标识符{item.Key}是升级指令,此处不允许下发。");
}
}
//检查设备是否有配置设备端物模型信息
//如果有配置就检查指令字典中是否有SpecialCommand标识符
//如果有就需要构建 SpecialCommand 的特别指令
if (deviceInfo.IsNeedConfigDeviceModel && deviceInfo.DeviceThingModelDataId.HasValue && input.Commands.ContainsKey(ThingModelFixedTypeConst.SpecialCommand))
{
var propertyInfo = await oneNETProductService.GetProductThingModelSpecialCommandDataTypeListAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
if (propertyInfo == null)
{
throw new UserFriendlyException($"{nameof(DeviceCommandInfoToOneNET)} OneNET设备属性设置失败产品Id{deviceInfo.IoTPlatformProductId}未找到对应的属性信息。");
}
Dictionary<string, string> tempSpecialCommand = await deviceThingModelService.BuildThingModelSpecialCommandAsync(propertyInfo, deviceInfo.DeviceThingModelDataId.Value);
input.Commands[ThingModelFixedTypeConst.SpecialCommand] = tempSpecialCommand;
}
else if (input.Commands.ContainsKey(ThingModelFixedTypeConst.SpecialCommand))
{
//设备端物模型解除绑定情况下SpecialCommand 还原默认值,设备也会恢复默认采集方式
//给SpecialCommand第一个参数设置默认值
var propertyInfo = await oneNETProductService.GetProductThingModelSpecialCommandDataTypeListAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
if (propertyInfo == null)
{
throw new UserFriendlyException($"{nameof(DeviceCommandInfoToOneNET)} OneNET设备属性设置失败产品Id{deviceInfo.IoTPlatformProductId}未找到对应的属性信息。");
}
string paName = propertyInfo.FirstOrDefault()?.Identifier;
input.Commands[ThingModelFixedTypeConst.SpecialCommand] = new Dictionary<string, object>() {
{ paName, CommonConst.SpecialCommandDefaultValue }
};
logger.LogWarning($"{nameof(DeviceCommandInfoToOneNET)} OneNET平台设备{deviceInfo.DeviceAddress}特殊抄读指令还原为{CommonConst.SpecialCommandDefaultValue}");
}
var commandRequest = new OpenApiRequest()
{
Message = input.Serialize(),
};
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, commandRequest, deviceInfo.Adapt<DeviceCacheInfos>(), input.Commands.Serialize());
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo));
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo);
return true;
}
catch (Exception ex)
{
logger.LogError($"{nameof(DeviceCommandInfoToOneNET)} 发送OneNET设备指令信息发生异常设备地址{deviceInfo.DeviceAddress},异常信息:{ex.Message}");
throw;
}
}
/// <summary>
/// 发送OneNET平台设备升级指令
/// </summary>
/// <param name="deviceInfo"></param>
/// <param name="taskInput">设置属性请求内容相关</param>
/// <param name="deviceFirmwareInfo">固件数据</param>
/// <param name="fileObject">固件文件信息</param>
/// <param name="input">入参</param>
/// <param name="productInfo">产品信息(如果已获取则传入以避免重复调用)</param>
/// <param name="platformThingModelInfo">平台端物模型信息(如果已获取则传入以避免重复调用)</param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
protected async Task<bool> DeviceUpgradeCommandToOneNET(DeviceManagementInfoDto deviceInfo, ReceiveCommandInfoDto taskInput, DeviceFirmwareInfoDto deviceFirmwareInfo, FileObjectDto fileObject, DeviceUpgradeForApiInput input, dynamic productInfo = null, dynamic platformThingModelInfo = null)
{
try
{
if (deviceInfo == null || deviceFirmwareInfo == null || fileObject == null)
{
throw new UserFriendlyException($"{nameof(DeviceUpgradeCommandToOneNET)}设备或固件信息不能为空");
}
if ((deviceInfo.IoTPlatformProductId != deviceFirmwareInfo.IoTPlatformProductId) || (deviceInfo.IoTPlatform != deviceFirmwareInfo.IoTPlatform))
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台产品信息固件中不一致");
}
//如果未传入产品信息和物模型信息,则获取
if (productInfo == null || platformThingModelInfo == null)
{
var (productInfoResult, platformThingModelInfoResult) = await GetOneNETProductAndThingModelInfoAsync(deviceInfo.IoTPlatformProductId, deviceInfo.DeviceAddress);
productInfo = productInfoResult;
platformThingModelInfo = platformThingModelInfoResult;
}
var upgradeProperty = ((List<IoTPlatformThingModelInfoDto>)platformThingModelInfo).Where(d => d.StandardFieldName == ThingModelFixedTypeConst.FIRMWARE_UPGRADE).FirstOrDefault();
if (upgradeProperty == null)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台端物模型信息不存在升级属性标识符{ThingModelFixedTypeConst.FIRMWARE_UPGRADE}。");
}
//构建升级指令《文件MD5值+OneNET产品KEY+升级标识符+文件大小》=>MD5计算获得签名值
var upgradeRecordInput = new CreateDeviceUpgradeRecordInput()
{
DeviceDataId = deviceInfo.Id,
DeviceName = deviceInfo.DeviceName,
DeviceAddress = deviceInfo.DeviceAddress,
OldFirmwareVersion = deviceInfo.FirmwareVersion,
NowFirmwareVersion = deviceFirmwareInfo.FirmwareVersion,
UpgradeSource = DeviceUpgradeSourceTypeEnum.AdminSystem,
UpgradeIdentifier = Yitter.IdGenerator.YitIdHelper.NextId(),
UpgradeDescription = input.UpgradeDescription,
UpgradeStatus = DeviceUpgradeStatusTypeEnum.NotUpgrade,
Id = GuidGenerator.Create()
};
var md5HashRaw = $"{fileObject.Md5Hash}{productInfo.ProductAccesskey}{upgradeRecordInput.UpgradeIdentifier}{fileObject.FileSize}";
var upgradeRequest = new DeviceUpgradeCommandRequest()
{
Length = fileObject.FileSize,
UpgradeIdentifier = upgradeRecordInput.UpgradeIdentifier,
SignatureValue = md5HashRaw.Md5Fun(),
FirmwareUrl = $"{serverApplicationOptions.DownloadDeviceFirmwareBasicUrl}{upgradeRecordInput.Id}",
TimeOut = serverApplicationOptions.DownloadDeviceFirmwareTimeOut,
};
upgradeRecordInput.UpgradeMessage = upgradeRequest.Serialize();
upgradeRecordInput.FirmwareSignature = upgradeRequest.SignatureValue;
string upgradeMessageHexString = upgradeRecordInput.UpgradeMessage.ToHexString();
//检查长度是否超过了OneNET平台升级属性标识符的长度限制
var thingModelInfoStr = ((OneNETProductInfoDto)productInfo).ThingModelInfos;
if (string.IsNullOrWhiteSpace(thingModelInfoStr))
{
logger.LogError($"{nameof(DeviceUpgradeCommandToOneNET)} 通过OneNET产品ID和属性标识符获取属性配置信息失败属性信息为空");
return false;
}
var thingModelInfo = thingModelInfoStr.Deserialize<OneNETAllThingModel>();
if (thingModelInfo.Properties == null)
{
logger.LogError($"{nameof(DeviceUpgradeCommandToOneNET)} 通过OneNET产品ID和属性标识符获取属性配置信息失败属性信息为空");
return false;
}
var property = thingModelInfo.Properties.Where(d => d.Identifier == upgradeProperty.IoTPlatformRawFieldName).FirstOrDefault(); //精准匹配
if (property == null)
{
logger.LogError($"{nameof(DeviceUpgradeCommandToOneNET)} 通过OneNET产品ID和属性标识符获取属性配置信息失败没有找到属性标识升级对应的属性平台信息");
return false;
}
var length = ((OneNETModelStringSpecs)property.DataType.Specs).Length;
if (upgradeMessageHexString.Length > length)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台端物模型信息属性标识符{upgradeProperty.IoTPlatformRawFieldName}长度超过OneNET平台限制请检查物模型属性标识符长度是否超过了{length}");
}
var insertResult = await deviceUpgradeRecordService.CreateAsync(upgradeRecordInput);
if (insertResult == null)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}升级记录失败");
}
//发送OneNET平台设备升级指令HEX格式字符串
taskInput.Commands = new Dictionary<string, object>()
{
{ upgradeProperty.IoTPlatformRawFieldName, upgradeMessageHexString }
};
taskInput.TelemetryCommandType = DeviceTelemetryCommandTypeEnum.FirmwareUpgrade;
var commandRequest = new OpenApiRequest()
{
Message = taskInput.Serialize(),
};
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, commandRequest, deviceInfo.Adapt<DeviceCacheInfos>(), taskInput.Commands.Serialize());
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETUpgradeCommandIssuedEventName, packetTaskInfo));
//将升级记录数据Id作为key文件数据ID作为值存入Redis缓存中。
string cacheKey = string.Format($"{RedisConst.CacheDeviceUpgradeRecordDataKey}", upgradeRecordInput.Id);
await FreeRedisProvider.Instance.SetAsync(cacheKey, fileObject.Id);
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo);
//更新状态为升级中
await deviceUpgradeRecordService.UpdateStatusAsync(new UpdateStatusInput()
{
Id = upgradeRecordInput.Id.Value,
UpgradeStatus = DeviceUpgradeStatusTypeEnum.Upgrading,
});
return true;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 抄读OneNET平台设备属性数据
/// </summary>
/// <param name="deviceInfo"></param>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
protected async Task<Dictionary<string, object>> DevicePropertyValueToOneNET(DeviceManagementInfoDto deviceInfo, DevicePropertyValueForApiInput input)
{
try
{
//检查下设备是否在线
var deviceOnlineStatus = await oneNETDeviceService.DeviceInfoDetailAsync(new DeviceInfoDetailInput()
{
DeviceName = deviceInfo.IoTPlatformDeviceOpenInfo,
OneNETAccountId = deviceInfo.IoTPlatformAccountId,
ProductId = deviceInfo.IoTPlatformProductId,
});
if (deviceOnlineStatus == null || deviceOnlineStatus.Code != ResponeResultEnum.Success)
{
throw new UserFriendlyException("获取平台设备信息失败");
}
if (deviceOnlineStatus.Data.Status != 1)
{
throw new UserFriendlyException("设备不在线");
}
var deviceDataResult = await oneNETDeviceService.QueryDevicePropertyDetail(new QueryDevicePropertyDetailInput()
{
DeviceName = deviceInfo.IoTPlatformDeviceOpenInfo,
OneNETAccountId = deviceInfo.IoTPlatformAccountId,
ProductId = deviceInfo.IoTPlatformProductId,
PropertyInfos = input.PropertyList
});
if (deviceDataResult == null || deviceDataResult.Success == false || deviceDataResult.Data == null)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceName}获取数据失败");
}
//获取设备产品平台端物模型信息
var platformThingModelInfo = await platformThingModelInfoAppService.FindByPlatformProductIdAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
if (platformThingModelInfo == null || platformThingModelInfo.Count <= 0)
{
return deviceDataResult.Data;
}
List<string> updateKeys = new List<string>()
{
ThingModelFixedTypeConst.FIRMWARE_VERSION.ToLowerInvariant(),
ThingModelFixedTypeConst.ReadingMode.ToLowerInvariant()
};
var platformUpdatePropertyInfos = platformThingModelInfo.Where(d => updateKeys.Contains(d.StandardFieldName.ToLowerInvariant())).ToList();
if (platformUpdatePropertyInfos == null || platformUpdatePropertyInfos.Count <= 0)
{
return deviceDataResult.Data;
}
//根据抄读结果(单个或多个属性)更新设备信息,统一收集后只调用一次 UpdateDeviceInfos
bool needUpdate = false;
//抄读结果如果有固件版本号,则更新固件版本号到设备信息中
var firmwareVersionKey = platformUpdatePropertyInfos.FirstOrDefault(d => d.StandardFieldName?.ToLowerInvariant() == ThingModelFixedTypeConst.FIRMWARE_VERSION.ToLowerInvariant())?.IoTPlatformRawFieldName;
if (!string.IsNullOrWhiteSpace(firmwareVersionKey) && deviceDataResult.Data.ContainsKey(firmwareVersionKey))
{
deviceInfo.FirmwareVersion = deviceDataResult.Data[firmwareVersionKey].ToString();
needUpdate = true;
}
//抄读结果如果有抄读模式,则更新抄读模式到设备信息中
var readModeKey = platformUpdatePropertyInfos.FirstOrDefault(d => d.StandardFieldName?.ToLowerInvariant() == ThingModelFixedTypeConst.ReadingMode.ToLowerInvariant())?.IoTPlatformRawFieldName;
if (!string.IsNullOrWhiteSpace(readModeKey) && deviceDataResult.Data.ContainsKey(readModeKey))
{
var readModeValue = deviceDataResult.Data[readModeKey];
// 兼容 JsonElement / 字符串 / 数值 三种情况
int readModeInt;
if (readModeValue is System.Text.Json.JsonElement jsonElement)
{
if (jsonElement.ValueKind == System.Text.Json.JsonValueKind.Number && jsonElement.TryGetInt32(out var num))
{
readModeInt = num;
}
else
{
readModeInt = Convert.ToInt32(jsonElement.ToString());
}
}
else
{
readModeInt = Convert.ToInt32(readModeValue);
}
deviceInfo.ReadingMode = (DeviceReadingModeEnum)readModeInt;
needUpdate = true;
}
if (needUpdate)
{
await deviceAppService.UpdateDeviceInfos(deviceInfo);
}
return deviceDataResult.Data;
}
catch (Exception ex)
{
if (input.PropertyList.Contains(ThingModelFixedTypeConst.SpecialCommand))
{
ex = new UserFriendlyException("请检查设备是否已经采集数据");
throw ex;
}
else
{
throw;
}
}
}
/// <summary>
/// 发送设备切换主站指令
/// </summary>
/// <param name="deviceInfo"></param>
/// <param name="taskInput">设置属性请求内容相关</param>
/// <param name="input">入参</param>
/// <param name="productInfo">产品信息(如果已获取则传入以避免重复调用)</param>
/// <param name="platformThingModelInfo">平台端物模型信息(如果已获取则传入以避免重复调用)</param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
protected async Task<bool> DeviceMasterSwitchCommand(DeviceManagementInfoDto deviceInfo, ReceiveCommandInfoDto taskInput, DeviceUpgradeForApiInput input, dynamic productInfo = null, dynamic platformThingModelInfo = null)
{
try
{
if (deviceInfo == null)
{
throw new UserFriendlyException($"{nameof(DeviceUpgradeCommandToOneNET)}设备或固件信息不能为空");
}
//如果未传入产品信息和物模型信息,则获取
if (productInfo == null || platformThingModelInfo == null)
{
var (productInfoResult, platformThingModelInfoResult) = await GetOneNETProductAndThingModelInfoAsync(deviceInfo.IoTPlatformProductId, deviceInfo.DeviceAddress);
productInfo = productInfoResult;
platformThingModelInfo = platformThingModelInfoResult;
}
var upgradeProperty = ((List<IoTPlatformThingModelInfoDto>)platformThingModelInfo).Where(d => d.StandardFieldName == ThingModelFixedTypeConst.DEVICE_TO_MASTER_SWITCH).FirstOrDefault();
if (upgradeProperty == null)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台端物模型信息不存在转主站属性标识符{ThingModelFixedTypeConst.DEVICE_TO_MASTER_SWITCH}。");
}
//构建转主站指令
var upgradeMessageHexString = "md5HashRaw".Md5Fun().ToHexString();
//发送OneNET平台设备升级指令HEX格式字符串
taskInput.Commands = new Dictionary<string, object>()
{
{ upgradeProperty.IoTPlatformRawFieldName, upgradeMessageHexString }
};
var commandRequest = new OpenApiRequest()
{
Message = taskInput.Serialize(),
};
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, commandRequest, deviceInfo.Adapt<DeviceCacheInfos>(), taskInput.Commands.Serialize());
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETUpgradeCommandIssuedEventName, packetTaskInfo));
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo);
return true;
}
catch (Exception)
{
throw;
}
}
#endregion
#region CTWing
/// <summary>
/// CTWing 批量设备创建
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected async Task<bool> CTWingDeviceBatchCreateAsync(BatchCreateDeviceAggregationInput input)
{
try
{
throw new UserFriendlyException($"CTWing 批量设备创建失败CTWing暂未实现。");
}
catch (Exception)
{
throw;
}
}
#endregion
/// <summary>
/// 更新设备信息并处理缓存
/// </summary>
/// <param name="input"></param>
/// <param name="pushResult">推送结果原始信息</param>
/// <param name="platformPassword">设备接入鉴权key</param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
private async Task<DeviceManagementInfoDto> DeviceUpdateHandler(DeviceManagementInfoDto input, HttpDataResult pushResult, string platformPassword = null)
{
UpdateDeviceInput updateDeviceInput = input.Adapt<UpdateDeviceInput>();
updateDeviceInput.IoTPlatformResponse = pushResult.Serialize();
updateDeviceInput.IsPlatformPushSuccess = true;
if (!string.IsNullOrWhiteSpace(updateDeviceInput.PlatformPassword))
{
updateDeviceInput.PlatformPassword = platformPassword;
}
var updateResult = await deviceAppService.UpdateAsync(updateDeviceInput);
if (updateResult == null)
{
logger.LogError($"{nameof(DeviceUpdateHandler)} 更新设备信息失败:{input.Serialize()}");
throw new UserFriendlyException($"推送结果更新失败。");
}
//设备数据缓存到Redis
DeviceCacheInfos deviceCacheInfos = input.Adapt<DeviceCacheInfos>();
deviceCacheInfos.PlatformPassword = null;
FreeRedisProvider.Instance.HSet<DeviceCacheInfos>(RedisConst.CacheAllDeviceInfoHashKey, input.DeviceAddress, deviceCacheInfos);
return input.Adapt<DeviceManagementInfoDto>();
}
/// <summary>
/// 获取OneNET产品信息和平台端物模型信息
/// </summary>
/// <param name="iotPlatformProductId">物联网平台产品Id</param>
/// <param name="deviceAddress">设备地址(用于错误信息)</param>
/// <returns>返回产品信息和平台端物模型信息的元组</returns>
protected async Task<(dynamic ProductInfo, dynamic PlatformThingModelInfo)> GetOneNETProductAndThingModelInfoAsync(string iotPlatformProductId, string deviceAddress)
{
//获取设备对应的产品信息
var productInfo = await oneNETProductService.GetProductInfoAsync(new IdInput<string>() { Id = iotPlatformProductId });
if (productInfo == null)
{
throw new UserFriendlyException($"{nameof(DeviceUpgradeCommandToOneNET)} OneNET设备升级属性设置失败产品Id{iotPlatformProductId}未找到对应的产品信息。");
}
//获取设备对应的平台端物模型信息,校验前端传入的属性标识集合是否存在不合法的属性标识符
var platformThingModelInfo = await platformThingModelInfoAppService.FindByPlatformProductIdAsync(new IdInput<string>() { Id = iotPlatformProductId });
if (platformThingModelInfo == null)
{
throw new UserFriendlyException($"设备{deviceAddress}的平台端物模型信息不存在。");
}
return (productInfo, platformThingModelInfo);
}
/// <summary>
/// 根据平台处理设备升级指令
/// </summary>
/// <param name="deviceInfo">设备信息</param>
/// <param name="receiveCommandInfoDto">接收指令信息Dto</param>
/// <param name="deviceFirmwareVersionInfo">固件版本信息</param>
/// <param name="fileInfo">文件信息</param>
/// <param name="input">升级输入参数</param>
/// <param name="oneNETProductInfo">OneNET产品信息可选如果已获取则传入以避免重复调用</param>
/// <param name="oneNETPlatformThingModelInfo">OneNET平台端物模型信息可选如果已获取则传入以避免重复调用</param>
/// <returns>处理结果</returns>
protected async Task<bool> ProcessDeviceUpgradeByPlatformAsync(DeviceManagementInfoDto deviceInfo, ReceiveCommandInfoDto receiveCommandInfoDto, DeviceFirmwareInfoDto deviceFirmwareVersionInfo, FileObjectDto fileInfo, DeviceUpgradeForApiInput input, dynamic oneNETProductInfo = null, dynamic oneNETPlatformThingModelInfo = null)
{
if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.OneNET)
{
//如果未传入产品信息和物模型信息,则获取
if (oneNETProductInfo == null || oneNETPlatformThingModelInfo == null)
{
var (productInfo, platformThingModelInfo) = await GetOneNETProductAndThingModelInfoAsync(deviceInfo.IoTPlatformProductId, deviceInfo.DeviceAddress);
oneNETProductInfo = productInfo;
oneNETPlatformThingModelInfo = platformThingModelInfo;
}
return await DeviceUpgradeCommandToOneNET(deviceInfo, receiveCommandInfoDto, deviceFirmwareVersionInfo, fileInfo, input, oneNETProductInfo, oneNETPlatformThingModelInfo);
}
else if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.CTWing)
{
//await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.CTWingAepCommandIssuedEventName,commandRequest);
//return true;
throw new UserFriendlyException($"发送设备升级指令信息失败CTWing暂未实现。");
}
else
{
throw new UserFriendlyException($"发送设备升级指令信息失败,未找到对应的产品配置信息。");
}
}
}
}

View File

@ -1,353 +0,0 @@
using Castle.Core.Logging;
using JiShe.IoT.IoTPlatformAggregation.Dto;
using JiShe.ServicePro.Commons;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.CTWingManagement.CTWingAccounts;
using JiShe.ServicePro.CTWingManagement.CTWingProducts;
using JiShe.ServicePro.DeviceManagement.ThingModels;
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.FreeRedisProvider;
using JiShe.ServicePro.OneNETManagement.OneNETAccounts;
using JiShe.ServicePro.OneNETManagement.OneNETProducts;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
namespace JiShe.IoT.IoTPlatformAggregation
{
/// <summary>
/// 平台聚合服务
/// </summary>
public class IoTPlatformAggregationService : IoTAppService, IIoTPlatformAggregationService
{
private readonly ILogger<IoTPlatformAggregationService> _logger;
private readonly ICTWingProductService _ctwingProductService;
private readonly IOneNETProductService _oneNetProductService;
private readonly ICTWingAccountService _ctwingAccountService;
private readonly IOneNETAccountService _oneNETAccountService;
private readonly IIoTPlatformThingModelInfoAppService _ioTPlatformThingModelInfoAppService;
public IoTPlatformAggregationService(ILogger<IoTPlatformAggregationService> logger,
ICTWingProductService ctwingProductService,
IOneNETProductService oneNetProductService,
ICTWingAccountService ctwingAccountService,
IOneNETAccountService oneNETAccountService,
IIoTPlatformThingModelInfoAppService ioTPlatformThingModelInfoAppService)
{
_logger = logger;
_ctwingProductService = ctwingProductService;
_oneNetProductService = oneNetProductService;
_ctwingAccountService = ctwingAccountService;
_oneNETAccountService = oneNETAccountService;
_ioTPlatformThingModelInfoAppService = ioTPlatformThingModelInfoAppService;
}
/// <summary>
/// 获取平台产品信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<List<IoTPlatformProductInfoOutput>> GetIoTPlatformProductInfoAsync(IoTPlatformProductInfoInput input
)
{
try
{
if (input == null)
{
throw new UserFriendlyException($"{nameof(GetIoTPlatformProductInfoAsync)} 平台聚合服务获取产品信息失败,参数异常");
}
List<IoTPlatformProductInfoOutput> ioTPlatformProductInfoOutputs = new List<IoTPlatformProductInfoOutput>();
if (input.IoTPlatformType == IoTPlatformTypeEnum.CTWing)
{
var ctwingProductInfos = await _ctwingProductService.GetAllListAsync(input.IoTPlatformAccount);
if (ctwingProductInfos == null)
{
return ioTPlatformProductInfoOutputs;
}
foreach (var ctwingProductInfo in ctwingProductInfos)
{
ioTPlatformProductInfoOutputs.Add(new IoTPlatformProductInfoOutput
{
IoTPlatformType = IoTPlatformTypeEnum.CTWing,
IoTPlatformProductId = ctwingProductInfo.IoTPlatformProductId,
ProductName = ctwingProductInfo.ProductName
});
}
}
if (input.IoTPlatformType == IoTPlatformTypeEnum.OneNET)
{
var oneNetProductInfos = await _oneNetProductService.GetAllListAsync(input.IoTPlatformAccount);
if (oneNetProductInfos == null)
{
return ioTPlatformProductInfoOutputs;
}
foreach (var oneNetProductInfo in oneNetProductInfos)
{
ioTPlatformProductInfoOutputs.Add(new IoTPlatformProductInfoOutput
{
IoTPlatformType = IoTPlatformTypeEnum.OneNET,
IoTPlatformProductId = oneNetProductInfo.IoTPlatformProductId,
ProductName = oneNetProductInfo.ProductName
});
}
}
return ioTPlatformProductInfoOutputs;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 获取平台账号信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<List<IoTPlatformAccountInfoOutput>> GetIoTPlatformAccountInfoAsync(IoTPlatformAccountInfoInput input
)
{
try
{
if (input == null)
{
throw new UserFriendlyException($"{nameof(GetIoTPlatformAccountInfoAsync)} 平台聚合服务获取账户信息失败,参数异常");
}
List<IoTPlatformAccountInfoOutput> ioTPlatformAccountInfoOutputs = new List<IoTPlatformAccountInfoOutput>();
if (input.IoTPlatformType == IoTPlatformTypeEnum.CTWing)
{
var ctwingAccountInfos = await _ctwingAccountService.GetAllListAsync();
if (ctwingAccountInfos == null)
{
return ioTPlatformAccountInfoOutputs;
}
foreach (var ctwingProductInfo in ctwingAccountInfos)
{
ioTPlatformAccountInfoOutputs.Add(new IoTPlatformAccountInfoOutput
{
IoTPlatformType = IoTPlatformTypeEnum.CTWing,
IoTPlatformAccount = ctwingProductInfo.AccountId,
IoTPlatformPhoneNumber = ctwingProductInfo.PhoneNumber
});
}
}
if (input.IoTPlatformType == IoTPlatformTypeEnum.OneNET)
{
var oneNetAccountInfos = await _oneNETAccountService.GetAllListAsync();
if (oneNetAccountInfos == null)
{
return ioTPlatformAccountInfoOutputs;
}
foreach (var oneNetProductInfo in oneNetAccountInfos)
{
ioTPlatformAccountInfoOutputs.Add(new IoTPlatformAccountInfoOutput
{
IoTPlatformType = IoTPlatformTypeEnum.OneNET,
IoTPlatformAccount = oneNetProductInfo.OneNETAccountId,
IoTPlatformPhoneNumber = oneNetProductInfo.PhoneNumber
});
}
}
return ioTPlatformAccountInfoOutputs;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 获取平台产品物模型属性信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<List<SelectResult>> GetIoTPlatformProductThingModelInfoAsync(IoTPlatformProductThingModelInfoInput input)
{
try
{
if (input == null)
{
throw new UserFriendlyException($"{nameof(GetIoTPlatformProductThingModelInfoAsync)} 平台产品聚合服务获取产品属性信息失败,参数异常");
}
var key = string.Format($"{RedisConst.CachePlatformProductThingModelInfoKey}", input.IoTPlatformType);
List<SelectResult> selectResults = new List<SelectResult>();
List<IoTPlatformProductInfoOutput> ioTPlatformProductInfoOutputs = new List<IoTPlatformProductInfoOutput>();
if (input.IoTPlatformType == IoTPlatformTypeEnum.CTWing)
{
//var ctwingProductInfos = await _ctwingProductService.GetAllListAsync();
//if (ctwingProductInfos == null)
//{
// return ioTPlatformProductInfoOutputs;
//}
//foreach (var ctwingProductInfo in ctwingProductInfos)
//{
// ioTPlatformProductInfoOutputs.Add(new IoTPlatformProductInfoOutput
// {
// IoTPlatformType = IoTPlatformTypeEnum.CTWing,
// IoTPlatformProductId = ctwingProductInfo.IoTPlatformProductId,
// ProductName = ctwingProductInfo.ProductName
// });
//}
_logger.LogError($"{nameof(GetIoTPlatformProductThingModelInfoAsync)}产品聚合服务暂不支持CTWing产品物模型属性信息查询");
return selectResults;
}
if (input.IoTPlatformType == IoTPlatformTypeEnum.OneNET)
{
var oneNETAllThingModel = FreeRedisProvider.Instance.HGet<OneNETAllThingModel>(key, input.IoTPlatformProductId);
if (oneNETAllThingModel != null)
{
if (!string.IsNullOrWhiteSpace(input.FiledType) && input.FiledType.ToLowerInvariant().Contains("event"))
{
var tempEventResult = OneNETAllThingModel.GetAllEventsSelectResult(oneNETAllThingModel.Events);
if (tempEventResult != null)
{
selectResults.AddRange(tempEventResult);
}
}
var tempPropertyCacheResult = OneNETAllThingModel.GetAllPropertiesSelectResult(oneNETAllThingModel.Properties);
if (tempPropertyCacheResult != null)
{
selectResults.AddRange(tempPropertyCacheResult);
}
return selectResults;
}
var oneNetProductInfos = await _oneNetProductService.GetProductInfoAsync(new IdInput<string>() { Id = input.IoTPlatformProductId });
if (oneNetProductInfos == null || string.IsNullOrWhiteSpace(oneNetProductInfos.ThingModelInfos))
{
return selectResults;
}
oneNETAllThingModel = oneNetProductInfos.ThingModelInfos.Deserialize<OneNETAllThingModel>();
if (oneNETAllThingModel == null)
{
return selectResults;
}
await FreeRedisProvider.Instance.HSetAsync<OneNETAllThingModel>(key, input.IoTPlatformProductId, oneNETAllThingModel);
if (!string.IsNullOrWhiteSpace(input.FiledType) && input.FiledType.ToLowerInvariant().Contains("event"))
{
var tempEventResult = OneNETAllThingModel.GetAllEventsSelectResult(oneNETAllThingModel.Events);
if (tempEventResult != null)
{
selectResults.AddRange(tempEventResult);
}
}
var tempPropertyResult = OneNETAllThingModel.GetAllPropertiesSelectResult(oneNETAllThingModel.Properties);
if (tempPropertyResult != null)
{
selectResults.AddRange(tempPropertyResult);
}
}
return selectResults;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 更新平台产品物模型属性信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<object> UpdateIoTPlatformProductThingModelInfoAsync(UpdateIoTPlatformProductThingModelInfoInput input
)
{
try
{
if (input == null)
{
throw new UserFriendlyException($"{nameof(UpdateIoTPlatformProductThingModelInfoAsync)} 平台产品聚合服务获取产品属性信息失败,参数异常");
}
if (input.IoTPlatformType == IoTPlatformTypeEnum.CTWing)
{
_logger.LogError($"{nameof(UpdateIoTPlatformProductThingModelInfoAsync)}产品聚合服务暂不支持CTWing产品物模型属性信息更新");
return null;
}
if (input.IoTPlatformType == IoTPlatformTypeEnum.OneNET)
{
var oneNetProductInfos = await _oneNetProductService.UpdateThingModelAsync(new IdInput<string>() { Id = input.IoTPlatformProductId });
if (oneNetProductInfos == null || string.IsNullOrWhiteSpace(oneNetProductInfos.ThingModelInfos))
{
_logger.LogError($"{nameof(UpdateIoTPlatformProductThingModelInfoAsync)}产品聚合服务OneNET产品{input.IoTPlatformProductId}物模型属性信息更新失败,-101");
return null;
}
var oneNETAllThingModel = oneNetProductInfos.ThingModelInfos.Deserialize<OneNETAllThingModel>();
if (oneNETAllThingModel == null)
{
_logger.LogError($"{nameof(UpdateIoTPlatformProductThingModelInfoAsync)}产品聚合服务OneNET产品{input.IoTPlatformProductId}物模型属性信息更新失败,-102");
return null;
}
List<SelectResult> selectResults = OneNETAllThingModel.GetAllPropertiesSelectResult(oneNETAllThingModel.Properties);
var tempEventSelectResults = OneNETAllThingModel.GetAllEventsSelectResult(oneNETAllThingModel.Events);
if (tempEventSelectResults != null)
{
selectResults.AddRange(tempEventSelectResults);
}
if (selectResults == null)
{
_logger.LogError($"{nameof(UpdateIoTPlatformProductThingModelInfoAsync)}产品聚合服务OneNET产品{input.IoTPlatformProductId}物模型属性信息更新失败,-103");
return null;
}
await _ioTPlatformThingModelInfoAppService.UpdatePlatformThingModelAsync(input.IoTPlatformProductId, input.IoTPlatformType, selectResults);
return oneNetProductInfos;
}
return null;
}
catch (Exception)
{
throw;
}
}
}
}

View File

@ -10,13 +10,13 @@ using System.Threading.Tasks;
namespace JiShe.IoT.Jobs
{
/// <summary>
/// 设备服务定时任务
/// 缓存设备数据到redis
/// </summary>
public class DeviceServerJob : SystemBackGroundWorkService
public class CacheDeviceDataToRedisJob : SystemBackGroundWorkService
{
private readonly ILogger<DeviceServerJob> _logger;
private readonly ILogger<CacheDeviceDataToRedisJob> _logger;
public readonly DeviceAppService _meterAppService;
public DeviceServerJob(ILogger<DeviceServerJob> logger, DeviceAppService meterAppService) : base(logger)
public CacheDeviceDataToRedisJob(ILogger<CacheDeviceDataToRedisJob> logger, DeviceAppService meterAppService) : base(logger)
{
_logger = logger;
_meterAppService = meterAppService;
@ -27,11 +27,9 @@ namespace JiShe.IoT.Jobs
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override async Task DoWorkAsync(CancellationToken cancellationToken)
protected override Task DoWorkAsync(CancellationToken cancellationToken)
{
//缓存数据到redis
await _meterAppService.CacheDeviceDataToRedisAsync();
await _meterAppService.CacheDeviceDataToRedisAsync();
return _meterAppService.CacheDeviceDataToRedisAsync();
}
protected override TimeSpan GetInterval()

View File

@ -2,14 +2,10 @@
using JiShe.ServicePro.Core;
using JiShe.ServicePro.DeviceManagement.DeviceInfos;
using JiShe.ServicePro.Dto;
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.FreeRedisProvider;
using JiShe.ServicePro.FreeSqlProvider;
using Mapster;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Polly;
using Volo.Abp;
using Volo.Abp.Auditing;
@ -22,21 +18,17 @@ namespace JiShe.ServicePro.OneNETManagement.Subscribers
public class ServiceCommunicationChannelSubscriberService : IoTAppService, IServiceCommunicationChannelSubscriberService
{
private readonly ILogger<ServiceCommunicationChannelSubscriberService> _logger;
private readonly IReliableRedisPubSubService _reliableRedisPubSubService;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IReliableRedisPubSubService _redisPubSubService;
/// <summary>
/// 设备状态统一订阅订阅服务
/// </summary>
/// <param name="logger"></param>
/// <param name="redisPubSubService"></param>
/// <param name="serviceScopeFactory"></param>
public ServiceCommunicationChannelSubscriberService(ILogger<ServiceCommunicationChannelSubscriberService> logger, IReliableRedisPubSubService redisPubSubService, IServiceScopeFactory serviceScopeFactory)
public ServiceCommunicationChannelSubscriberService(ILogger<ServiceCommunicationChannelSubscriberService> logger, IReliableRedisPubSubService redisPubSubService)
{
_logger = logger;
_reliableRedisPubSubService = redisPubSubService;
_serviceScopeFactory = serviceScopeFactory;
_redisPubSubService = redisPubSubService;
}
/// <summary>
@ -47,18 +39,15 @@ namespace JiShe.ServicePro.OneNETManagement.Subscribers
{
try
{
// 为订阅回调创建独立的 FreeSql 客户端
var callbackFreeSqlDbContext = FreeSqlDbContext;
var callbackFreeSql = FreeRedisProvider;
// 订阅频道
await _reliableRedisPubSubService.SubscribeReliableAsync<DeviceStatusMessage>(DistributedMessageCenterConst.ServiceCommunicationDeviceStatusEventName, async (message) =>
await _redisPubSubService.SubscribeReliableAsync<DeviceStatusMessage>(DistributedMessageCenterConst.ServiceCommunicationDeviceStatusEventName, async (message) =>
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var sp = scope.ServiceProvider;
var scopedServiceCommunicationChannelSubscriberService = sp.GetRequiredService<IServiceCommunicationChannelSubscriberService>();
var scopedLogger = sp.GetRequiredService<ILogger<ServiceCommunicationChannelSubscriberService>>();
scopedLogger.LogError($"{nameof(ServiceCommunicationDeviceStatusSubscriber)}收到设备{message.DeviceAddress}上下线状态更新通知,开始更新{message.Status}状态处理...");
await scopedServiceCommunicationChannelSubscriberService.DeviceOnlineStatusUpdate(message);
scopedLogger.LogError($"{nameof(ServiceCommunicationDeviceStatusSubscriber)}设备{message.DeviceAddress}上下线状态更新通知,结束更新{message.Status}状态处理...");
}
await HandDeviceStatus(message, callbackFreeSqlDbContext, callbackFreeSql);
return true;
});
}
@ -72,44 +61,46 @@ namespace JiShe.ServicePro.OneNETManagement.Subscribers
/// <summary>
/// 设备状态处理
/// </summary>
/// <param name="input"></param>
/// <param name="deviceStatusMessage"></param>
/// <param name="callbackFreeSqlDbContext"></param>
/// <param name="callbackFreeRedisProvider"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task DeviceOnlineStatusUpdate(DeviceStatusMessage input)
private async Task HandDeviceStatus(DeviceStatusMessage deviceStatusMessage, IFreeSqlProvider callbackFreeSqlDbContext, IFreeRedisProvider callbackFreeRedisProvider)
{
try
{
if (input == null || input.DeviceAddress.IsNullOrWhiteSpace())
if (deviceStatusMessage == null || callbackFreeSqlDbContext == null)
{
throw new UserFriendlyException($"设备状态消息处理失败,参数为空");
}
var deviceEntity = await FreeSqlDbContext.Instance.Select<DeviceManagementInfo>()
.Where(d => d.IoTPlatform == input.IoTPlatform && d.DeviceAddress == input.DeviceAddress)
var deviceEntity = await callbackFreeSqlDbContext.Instance.Select<DeviceManagementInfo>()
.Where(d => d.IoTPlatform == deviceStatusMessage.IoTPlatform && d.DeviceAddress == deviceStatusMessage.DeviceAddress)
.FirstAsync();
if (deviceEntity == null)
{
throw new UserFriendlyException($"{input.DeviceAddress} 设备不存在");
throw new UserFriendlyException($"{deviceStatusMessage.DeviceAddress} 设备不存在");
}
deviceEntity.DeviceOnlineStatus = input.Status;
if (input.Status == DeviceOnlineStatus.Online)
deviceEntity.DeviceOnlineStatus = deviceStatusMessage.Status;
if (deviceStatusMessage.Status == DeviceOnlineStatusEnum.Online)
{
deviceEntity.LastOnlineTime = TimestampHelper.ConvertToDateTime(input.ReceivedTime, TimestampUnit.Milliseconds, DateTimeKind.Local);
deviceEntity.LastOnlineTime = TimestampHelper.ConvertToDateTime(deviceStatusMessage.ReceivedTime, TimestampUnit.Milliseconds, DateTimeKind.Local);
}
else if (input.Status == DeviceOnlineStatus.Offline)
else if (deviceStatusMessage.Status == DeviceOnlineStatusEnum.Offline)
{
deviceEntity.LastOfflineTime = TimestampHelper.ConvertToDateTime(input.ReceivedTime, TimestampUnit.Milliseconds, DateTimeKind.Local);
deviceEntity.LastOfflineTime = TimestampHelper.ConvertToDateTime(deviceStatusMessage.ReceivedTime, TimestampUnit.Milliseconds, DateTimeKind.Local);
}
var updateResult = await FreeSqlDbContext.Instance.Update<DeviceManagementInfo>()
var updateResult = await callbackFreeSqlDbContext.Instance.Update<DeviceManagementInfo>()
.SetSource(deviceEntity)
.UpdateColumns(a => new { a.DeviceOnlineStatus, a.LastOnlineTime, a.LastOfflineTime })
.ExecuteAffrowsAsync();
if (updateResult <= 0)
{
_logger.LogError($"{nameof(ServiceCommunicationDeviceStatusSubscriber)} 设备状态更新失败{input.Serialize()}");
_logger.LogError($"{nameof(ServiceCommunicationDeviceStatusSubscriber)} 设备状态更新失败{deviceStatusMessage.Serialize()}");
throw new UserFriendlyException($"设备状态更新失败");
}
@ -118,38 +109,7 @@ namespace JiShe.ServicePro.OneNETManagement.Subscribers
DeviceCacheInfos deviceCacheInfos = deviceEntity.Adapt<DeviceCacheInfos>();
deviceCacheInfos.PlatformPassword = null;
FreeRedisProvider.Instance.HSet<DeviceCacheInfos>(RedisConst.CacheAllDeviceInfoHashKey, deviceEntity.DeviceAddress, deviceCacheInfos);
}
catch (Exception ex)
{
_logger.LogError($"{nameof(DeviceOnlineStatusUpdate)} 设备在线状态更新发生异常:{ex.Message}");
throw ;
}
}
/// <summary>
/// 设备升级结果通知 Redis 消息订阅
/// </summary>
/// <returns></returns>
public async Task ServiceCommunicationDeviceUpgradeSubscriber()
{
try
{
// 订阅频道
await _reliableRedisPubSubService.SubscribeReliableAsync<UpgradeResultMessage>(DistributedMessageCenterConst.ServiceCommunicationDeviceUpgradeEventName, async (message) =>
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var sp = scope.ServiceProvider;
var scopedServiceCommunicationChannelSubscriberService = sp.GetRequiredService<IServiceCommunicationChannelSubscriberService>();
var scopedLogger = sp.GetRequiredService<ILogger<ServiceCommunicationChannelSubscriberService>>();
scopedLogger.LogError($"{nameof(ServiceCommunicationDeviceUpgradeSubscriber)}收到设备{message.DeviceAddress} 升级结果更新通知,开始更新{message.ResultType}状态处理...");
await scopedServiceCommunicationChannelSubscriberService.DeviceUpgradeResultUpdate(message);
scopedLogger.LogError($"{nameof(ServiceCommunicationDeviceUpgradeSubscriber)}设备{message.DeviceAddress} 升级结果更新通知,结束更新{message.ResultType}状态处理...");
}
return true;
});
callbackFreeRedisProvider.Instance.HSet<DeviceCacheInfos>(RedisConst.CacheAllDeviceInfoHashKey, deviceEntity.DeviceAddress, deviceCacheInfos);
}
catch (Exception)
{
@ -157,77 +117,5 @@ namespace JiShe.ServicePro.OneNETManagement.Subscribers
throw;
}
}
/// <summary>
/// 设备升级结果更新
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task DeviceUpgradeResultUpdate(UpgradeResultMessage input)
{
try
{
if (input == null || input.DeviceAddress.IsNullOrWhiteSpace())
{
throw new UserFriendlyException($"设备升级结果消息处理失败,参数为空");
}
var currentTime = DateTime.Now;
var deviceUpgradeRecordEntity = await FreeSqlDbContext.Instance.Select<DeviceUpgradeRecord>()
.Where(d => d.UpgradeIdentifier == input.UpgradeIdentifier && d.DeviceAddress == input.DeviceAddress)
.FirstAsync();
if (deviceUpgradeRecordEntity == null)
{
throw new UserFriendlyException($"设备 {input.DeviceAddress} 的标识符{input.UpgradeIdentifier}升级记录不存在");
}
if (input.ResultType == DeviceUpgradeResultTypeEnum.Success)
{
deviceUpgradeRecordEntity.UpgradeResult = input.ResultType;
deviceUpgradeRecordEntity.UpgradeStatus = DeviceUpgradeStatusTypeEnum.UpgradeSuccess;
deviceUpgradeRecordEntity.UpgradeDate = TimestampHelper.ConvertToDateTime(input.ReceivedTime, TimestampUnit.Milliseconds, DateTimeKind.Local);
//更新成功后,更新设备固件版本号
await FreeSqlDbContext.Instance.Update<DeviceManagementInfo>()
.Set(d=>d.FirmwareVersion, deviceUpgradeRecordEntity.NowFirmwareVersion)
.Set(d => d.UpgradeDate, currentTime)
.Set(d => d.LastModificationTime, currentTime)
.Set(d => d.LastModifierId, CurrentUser.Id)
.Where(d=>d.Id == deviceUpgradeRecordEntity.DeviceDataId)
.ExecuteAffrowsAsync();
}
else
{
deviceUpgradeRecordEntity.UpgradeResult = input.ResultType;
deviceUpgradeRecordEntity.UpgradeStatus = DeviceUpgradeStatusTypeEnum.UpgradeFailed;
deviceUpgradeRecordEntity.UpgradeDate = TimestampHelper.ConvertToDateTime(input.ReceivedTime, TimestampUnit.Milliseconds, DateTimeKind.Local);
}
var updateResult = await FreeSqlDbContext.Instance.Update<DeviceUpgradeRecord>()
.SetSource(deviceUpgradeRecordEntity)
.UpdateColumns(a => new { a.UpgradeResult, a.UpgradeStatus, a.UpgradeDate })
.ExecuteAffrowsAsync();
if (updateResult <= 0)
{
_logger.LogError($"{nameof(ServiceCommunicationDeviceStatusSubscriber)} 设备状态更新失败{input.Serialize()}");
throw new UserFriendlyException($"设备状态更新失败");
}
//更新设备数据缓存到Redis
DeviceCacheInfos deviceCacheInfos = deviceUpgradeRecordEntity.Adapt<DeviceCacheInfos>();
deviceCacheInfos.PlatformPassword = null;
FreeRedisProvider.Instance.HSet<DeviceCacheInfos>(RedisConst.CacheAllDeviceInfoHashKey, deviceUpgradeRecordEntity.DeviceAddress, deviceCacheInfos);
}
catch (Exception ex)
{
_logger.LogError($"{nameof(DeviceUpgradeResultUpdate)} 设备升级结果状态更新发生异常:{ex.Message}");
throw;
}
}
}
}

View File

@ -104,10 +104,10 @@ namespace JiShe.IoT.Workshops
.OrderByDescending(e => e.CreationTime)
.ToListAsync(d => new WorkshopProductListOutput
{
IoTPlatformProductId = d.IoTPlatformProductId,
IoTPlatformProductName = d.ProductName,
ProductDataId = d.Id,
ProductName = d.ProductName,
CommunicationAddress = d.CommunicationAddress,
IoTPlatformProductAccesskey = d.FeatureAccesskey,
ProductAccesskey = d.FeatureAccesskey,
});
}
else if (messageBody.IoTPlatform == IoTPlatformTypeEnum.CTWing)
@ -117,10 +117,10 @@ namespace JiShe.IoT.Workshops
.OrderByDescending(e => e.CreationTime)
.ToListAsync(d => new WorkshopProductListOutput
{
IoTPlatformProductId = d.IoTPlatformProductId,
IoTPlatformProductName = d.ProductName,
ProductDataId = d.Id,
ProductName = d.ProductName,
CommunicationAddress = d.CommunicationAddress,
IoTPlatformProductAccesskey = d.ProductAccesskey,
ProductAccesskey = d.ProductAccesskey,
CommunicationAddressTLS = d.CommunicationAddressTLS
});
}

View File

@ -1,15 +1,14 @@
{
"ConnectionStrings": {
//"Default": "Data Source=192.168.111.174;Port=13306;Database=JiSheIoTProDB386;uid=root;pwd=JiShe!aqG#5kGgh&0;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;TreatTinyAsBoolean=false;SslMode=None;Pooling=true;"
"Default": "Host=192.168.111.174;Port=5432;Database=jisheiotprodb;Username=postgres;Password=JiShe!aqG#5kGgh&0;Encoding=UTF8;Pooling=true;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=100;"
"Default": "Data Source=localhost;Port=33306;Database=JiSheIoTProDB54;uid=root;pwd=123456789;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;TreatTinyAsBoolean=false;SslMode=None;Pooling=true;"
},
"IoTDBOptions": {
"UserName": "root",
"Password": "Lixiao@1980",
//"TreeModelClusterList": [ "192.168.111.174:6667", "47.110.60.222:6667", "47.110.62.104:6667" ],
//"TableModelClusterList": [ "192.168.111.174:6667", "47.110.60.222:6667", "47.110.62.104:6667" ],
"TreeModelClusterList": [ "192.168.111.174:30710" ],
"TableModelClusterList": [ "192.168.111.174:30710" ],
//"TreeModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ],
//"TableModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ],
"TreeModelClusterList": [ "47.110.53.196:30710" ],
"TableModelClusterList": [ "47.110.53.196:30710" ],
"PoolSize": 32,
"DataBaseName": "jisheiotdata",
"OpenDebugMode": false,

View File

@ -23,7 +23,6 @@
<ProjectReference Include="..\..\JiShe.ServicePro\modules\TemplateManagement\src\JiShe.ServicePro.TemplateManagement.Domain.Shared\JiShe.ServicePro.TemplateManagement.Domain.Shared.csproj" />
<ProjectReference Include="..\..\JiShe.ServicePro\modules\OneNETManagement\src\JiShe.ServicePro.OneNETManagement.Domain.Shared\JiShe.ServicePro.OneNETManagement.Domain.Shared.csproj" />
<ProjectReference Include="..\..\JiShe.ServicePro\frameworks\src\JiShe.ServicePro.Core\JiShe.ServicePro.Core.csproj" />
<ProjectReference Include="..\..\JiShe.ServicePro\shared\JiShe.ServicePro.Shared.DynamicAnalyzerEngine\JiShe.ServicePro.Shared.DynamicAnalyzerEngine.csproj" />
</ItemGroup>
@ -40,7 +39,6 @@
<PackageReference Include="JiShe.ServicePro.Core" />
<PackageReference Include="JiShe.ServicePro.CTWingManagement.Domain.Shared" />
<PackageReference Include="JiShe.ServicePro.OneNETManagement.Domain.Shared" />
<PackageReference Include="JiShe.ServicePro.Shared.DynamicAnalyzerEngine" />
</ItemGroup>
<ItemGroup>

View File

@ -49,7 +49,7 @@ namespace JiShe.IoT.Domain.Shared
/// 扩展属性,用于存储自定义字段,JSON格式
/// </summary>
[Comment("扩展属性,用于存储自定义字段,JSON格式")]
[JsonMap, Column(DbType = "jsonb")]
[JsonMap]
public new ExtraPropertyDictionary ExtraProperties { get; set; } = new ExtraPropertyDictionary();

View File

@ -16,6 +16,7 @@ global using JiShe.IoT.Users.Dto;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Logging.Abstractions;
global using Volo.Abp.AutoMapper;
global using Volo.Abp.Data;
global using Volo.Abp.DependencyInjection;
global using Volo.Abp.Domain.Services;

View File

@ -29,6 +29,7 @@ namespace JiShe.IoT
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpMultiTenancyOptions>(options => { options.IsEnabled = MultiTenancyConsts.IsEnabled; });
Configure<AbpAutoMapperOptions>(options => { options.AddMaps<IoTDomainModule>(); });
}
}
}

View File

@ -23,5 +23,14 @@ namespace JiShe.IoT
protected IDistributedEventBus DistributedEventBus =>
LazyServiceProvider.LazyGetRequiredService<IDistributedEventBus>();
/// <summary>
/// 对象映射器
/// </summary>
protected IObjectMapper ObjectMapper => LazyServiceProvider.LazyGetService<IObjectMapper>(
provider =>
ObjectMapperContext == null
? provider.GetRequiredService<IObjectMapper>()
: (IObjectMapper)provider.GetRequiredService(
typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext)));
}
}

View File

@ -69,12 +69,11 @@ namespace JiShe.IoT.EntityFrameworkCore
// 设备管理
public DbSet<DeviceManagementInfo> DeviceManagementInfo { get; set; }
public DbSet<SubDeviceManagementInfo> SubDeviceManagementInfo { get; set; }
public DbSet<DeviceThingModelManagement> DeviceThingModelManagement { get; set; }
public DbSet<DeviceThingModelPropertyInfo> DeviceThingModelDetailInfo { get; set; }
public DbSet<DeviceThingModelCommandInfo> DeviceThingModelCommandInfo { get; set; }
public DbSet<DeviceFirmwareInfo> DeviceFirmwareInfo { get; set; }
public DbSet<DeviceUpgradeRecord> DeviceUpgradeRecord { get; set; }
/// <summary>
/// 平台端物模型信息
@ -90,6 +89,7 @@ namespace JiShe.IoT.EntityFrameworkCore
public DbSet<OneNETProductInfos> OneNETProductInfo { get; set; }
public IoTDbContext(DbContextOptions<IoTDbContext> options)
: base(options)
{

View File

@ -1,18 +1,16 @@
using JiShe.ServicePro.CTWingManagement.EntityFrameworkCore;
using JiShe.ServicePro.CTWingManagement.EntityFrameworkCore;
using JiShe.ServicePro.DeviceManagement.EntityFrameworkCore;
using JiShe.ServicePro.DynamicMenuManagement.EntityFrameworkCore;
using JiShe.ServicePro.FileManagement.EntityFrameworkCore;
using JiShe.ServicePro.OneNETManagement.EntityFrameworkCore;
using JiShe.ServicePro.TemplateManagement.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.Guids;
using Volo.Abp.Timing;
namespace JiShe.IoT.EntityFrameworkCore
{
[DependsOn(
typeof(IoTDomainModule),
typeof(AbpEntityFrameworkCorePostgreSqlModule),
typeof(AbpEntityFrameworkCoreMySQLPomeloModule),
typeof(BasicManagementEntityFrameworkCoreModule),
typeof(DataDictionaryManagementEntityFrameworkCoreModule),
typeof(NotificationManagementEntityFrameworkCoreModule),
@ -29,19 +27,10 @@ namespace JiShe.IoT.EntityFrameworkCore
public override void PreConfigureServices(ServiceConfigurationContext context)
{
IoTEfCoreEntityExtensionMappings.Configure();
// ✅ 启用 Npgsql 旧版时间戳行为
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
// ✅ 配置 ABP 使用 UTC 时间
Configure<AbpClockOptions>(options =>
{
options.Kind = DateTimeKind.Utc;
});
context.Services.AddAbpDbContext<IoTDbContext>(options =>
{
/* Remove "includeAllEntities: true" to create
@ -58,7 +47,10 @@ namespace JiShe.IoT.EntityFrameworkCore
* See also HayoonKoreaDbContextFactory for EF Core tooling.
* https://github.com/abpframework/abp/issues/21879
* */
options.UseNpgsql();
options.UseMySQL(builder =>
{
builder.TranslateParameterizedCollectionsToConstants();
});
});
}
}

View File

@ -11,8 +11,9 @@ namespace JiShe.IoT.EntityFrameworkCore
IoTEfCoreEntityExtensionMappings.Configure();
var configuration = BuildConfiguration();
var serverVersion = new MySqlServerVersion(new Version(8, 4, 6));
var builder = new DbContextOptionsBuilder<IoTDbContext>()
.UseNpgsql(configuration.GetConnectionString("Default"));
.UseMySql(configuration.GetConnectionString("Default") , serverVersion);
return new IoTDbContext(builder.Options);
}

View File

@ -30,6 +30,7 @@ global using Volo.Abp.BackgroundJobs;
global using Volo.Abp.Data;
global using Volo.Abp.DependencyInjection;
global using Volo.Abp.EntityFrameworkCore;
global using Volo.Abp.EntityFrameworkCore.MySQL;
global using Volo.Abp.FeatureManagement;
global using Volo.Abp.Identity;
global using Volo.Abp.Modularity;

View File

@ -6,10 +6,7 @@
<ItemGroup>
<ProjectReference Include="..\JiShe.IoT.Domain\JiShe.IoT.Domain.csproj" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.PostgreSql" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="5.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="5.0.0" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.MySQL.Pomelo" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Debug'" Label="Debug">

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JiShe.IoT.Migrations
{
/// <inheritdoc />
public partial class updatedevicethingmodel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsEnable",
table: "ServiceProDeviceThingModelCommandInfo",
type: "boolean",
nullable: false,
defaultValue: false,
comment: "是否启用");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsEnable",
table: "ServiceProDeviceThingModelCommandInfo");
}
}
}

View File

@ -1,30 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JiShe.IoT.Migrations
{
/// <inheritdoc />
public partial class updatedeviceinfo20260128 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "ReadingMode",
table: "ServiceProDeviceInfo",
type: "integer",
nullable: false,
defaultValue: 1,
comment: "抄读模式");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ReadingMode",
table: "ServiceProDeviceInfo");
}
}
}

View File

@ -1,28 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JiShe.IoT.Migrations
{
/// <inheritdoc />
public partial class updatedeviceinfo202601281007 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "IsNeedConfigDevicMdoel",
table: "ServiceProDeviceInfo",
newName: "IsNeedConfigDeviceModel");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "IsNeedConfigDeviceModel",
table: "ServiceProDeviceInfo",
newName: "IsNeedConfigDevicMdoel");
}
}
}

View File

@ -18,11 +18,11 @@ namespace JiShe.IoT.Controllers
/// <summary>
/// 接收业务系统指令信息
/// </summary>
[HttpPost(nameof(ReceiveSetCommandInfoAsync))]
[HttpPost(nameof(ReceiveCommandInfoAsync))]
[SwaggerOperation(summary: "接收业务系统指令信息", Tags = new[] { "AggregationBusiness" })]
public async Task<HttpDataResult> ReceiveSetCommandInfoAsync(OpenApiRequest input)
public async Task<HttpDataResult> ReceiveCommandInfoAsync(OpenApiRequest input)
{
return await _businessSystemAggregationService.ReceiveSetCommandInfoAsync(input);
return await _businessSystemAggregationService.ReceiveCommandInfoAsync(input);
}
/// <summary>
@ -45,15 +45,5 @@ namespace JiShe.IoT.Controllers
{
return await _businessSystemAggregationService.BatchCreateDeviceInfoAsync(input);
}
/// <summary>
/// 接收业务系统获取属性指令信息
/// </summary>
[HttpPost(nameof(ReceiveGetCommandInfoAsync))]
[SwaggerOperation(summary: "接收业务系统获取属性指令信息", Tags = new[] { "AggregationBusiness" })]
public async Task<HttpDataResult> ReceiveGetCommandInfoAsync(OpenApiRequest input)
{
return await _businessSystemAggregationService.ReceiveGetCommandInfoAsync(input);
}
}
}

View File

@ -3,9 +3,6 @@ using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.ServicePro;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.DeviceManagement.DeviceInfos;
using JiShe.ServicePro.DeviceManagement.DeviceInfos.Dto;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Content;
namespace JiShe.IoT.Controllers
{
@ -90,67 +87,5 @@ namespace JiShe.IoT.Controllers
return _deviceAggregationService.DeviceCommandForApiAsync(input);
}
/// <summary>
/// 获取设备属性最新值
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost(nameof(GetDevicePropertyValueForApiAsync))]
[SwaggerOperation(summary: "获取设备属性最新值", Tags = new[] { "AggregationDevice" })]
public Task<Dictionary<string, object>> GetDevicePropertyValueForApiAsync(DevicePropertyValueForApiInput input)
{
return _deviceAggregationService.GetDevicePropertyValueForApiAsync(input);
}
/// <summary>
/// 发送设备升级指令信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost(nameof(DeviceUpgradeForApiAsync))]
[SwaggerOperation(summary: "发送设备升级指令信息", Tags = new[] { "AggregationDevice" })]
public Task<bool> DeviceUpgradeForApiAsync(DeviceUpgradeForApiInput input)
{
return _deviceAggregationService.DeviceUpgradeForApiAsync(input);
}
/// <summary>
/// 批量发送设备升级指令信息,只对同一个平台下,同一个产品下的设备进行固件升级信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost(nameof(DeviceBatchUpgradeForApiAsync))]
[SwaggerOperation(summary: "批量发送设备升级指令信息,只对同一个平台下,同一个产品下的设备进行固件升级信息", Tags = new[] { "AggregationDevice" })]
public Task<List<string>> DeviceBatchUpgradeForApiAsync(DeviceBatchUpgradeForApiInput input)
{
return _deviceAggregationService.DeviceBatchUpgradeForApiAsync(input);
}
/// <summary>
/// 下载设备固件文件
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("DownloadFirmware")]
[HttpGet("DownloadFirmware")]
[SwaggerOperation(summary: "下载设备固件文件", Tags = new[] { "AggregationDevice" })]
[AllowAnonymous]
public Task<RemoteStreamContent> DownloadFirmwareInfoAsync(IdInput input)
{
return _deviceAggregationService.DownloadFirmwareInfoAsync(input);
}
/// <summary>
/// 绑定设备端物模型
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost(nameof(BindingDeviceThingModel))]
[SwaggerOperation(summary: "绑定设备端物模型", Tags = new[] { "DeviceInfo" })]
public async Task<bool> BindingDeviceThingModel(BindingDeviceThingModelInput input)
{
return await _deviceAggregationService.BindingDeviceThingModel(input);
}
}
}

View File

@ -1,73 +0,0 @@
using JiShe.IoT.DeviceAggregation;
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.IoT.IoTPlatformAggregation;
using JiShe.IoT.IoTPlatformAggregation.Dto;
using JiShe.ServicePro;
using JiShe.ServicePro.Commons;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.DeviceManagement.DeviceInfos;
namespace JiShe.IoT.Controllers
{
/// <summary>
/// 平台产品聚合服务
/// </summary>
[Route("/Aggregation/IoTPlatform")]
public class IoTPlatformAggregationController : IoTController
{
private readonly IIoTPlatformAggregationService _iotPlatformAggregationService;
public IoTPlatformAggregationController(IIoTPlatformAggregationService deviceAggregationService)
{
_iotPlatformAggregationService = deviceAggregationService;
}
/// <summary>
/// 获取平台产品信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost(nameof(GetIoTPlatformProductInfoAsync))]
[SwaggerOperation(summary: "获取平台产品信息", Tags = new[] { "AggregationIoTPlatform" })]
public async Task<List<IoTPlatformProductInfoOutput>> GetIoTPlatformProductInfoAsync(IoTPlatformProductInfoInput input)
{
return await _iotPlatformAggregationService.GetIoTPlatformProductInfoAsync(input);
}
/// <summary>
/// 获取平台账号信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost(nameof(GetIoTPlatformAccountInfoAsync))]
[SwaggerOperation(summary: "获取平台账号信息", Tags = new[] { "AggregationIoTPlatform" })]
public async Task<List<IoTPlatformAccountInfoOutput>> GetIoTPlatformAccountInfoAsync(IoTPlatformAccountInfoInput input)
{
return await _iotPlatformAggregationService.GetIoTPlatformAccountInfoAsync(input);
}
/// <summary>
/// 获取平台产品物模型信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost(nameof(GetIoTPlatformProductThingModelInfoAsync))]
[SwaggerOperation(summary: "获取平台产品物模型信息", Tags = new[] { "AggregationIoTPlatform" })]
public async Task<List<SelectResult>> GetIoTPlatformProductThingModelInfoAsync(IoTPlatformProductThingModelInfoInput input)
{
return await _iotPlatformAggregationService.GetIoTPlatformProductThingModelInfoAsync(input);
}
/// <summary>
/// 更新平台产品物模型信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost(nameof(UpdateIoTPlatformProductThingModelInfoAsync))]
[SwaggerOperation(summary: "更新平台产品物模型信息", Tags = new[] { "AggregationIoTPlatform" })]
public async Task<object> UpdateIoTPlatformProductThingModelInfoAsync(UpdateIoTPlatformProductThingModelInfoInput input)
{
return await _iotPlatformAggregationService.UpdateIoTPlatformProductThingModelInfoAsync(input);
}
}
}