update:下发数采

This commit is contained in:
2025-12-04 12:46:13 +08:00
parent a9399f5497
commit 65f6b635d8
13 changed files with 801 additions and 149 deletions

View File

@@ -88,9 +88,9 @@ public class SnowMachineController {
*/
@GetMapping("/")
@Operation(summary = "获取所有造雪机设备列表")
public HttpResponseData<List<SnowMachineDevice>> getAllSnowMachines() {
public HttpResponseData<List<SnowMachineDevice>> getAllSnowMachines(@RequestParam(required = false) String keyword) {
// 调用服务层获取所有设备
List<SnowMachineDevice> result = snowMachineDeviceService.getAllSnowMachineDevices();
List<SnowMachineDevice> result = snowMachineDeviceService.getAllSnowMachineDevices(keyword);
return HttpResponseData.ok(result);
}
}

View File

@@ -0,0 +1,40 @@
package com.ski.lichuan.admin.controller.monitor;
import com.ski.lichuan.model.common.HttpResponseData;
import com.ski.lichuan.model.dashboard.SnowMachineWorkingInfo;
import com.ski.lichuan.model.device.Impl.SnowMachineDevice;
import com.ski.lichuan.services.DeviceMonitorService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/monitor")
@CrossOrigin(origins = "*") // 根据实际需要调整CORS策略
@Tag(name = "设备监控", description = "设备监控信息展示")
public class DeviceMonitorController {
@Autowired
private DeviceMonitorService deviceMonitorService;
/**
* 获取设备监控信息列表,支持按名称,雪道,优先级
* @return 设备监控信息
*/
@GetMapping("/list")
@Operation(summary = "获取设备监控信息列表", description = "根据名称,雪道,运营线查询设备监控信息")
public HttpResponseData<List<SnowMachineWorkingInfo>> getDeviceMonitorList(
@RequestParam(required = false) String name,
@RequestParam(required = false) Integer trailId,
@RequestParam(required = false) Integer priority) {
HttpResponseData<List<SnowMachineWorkingInfo>> responseData = new HttpResponseData<>();
List<SnowMachineWorkingInfo> snowDevices = deviceMonitorService.getDeviceMonitorList(name, trailId, priority);
return responseData.success(snowDevices);
}
}

View File

@@ -1,11 +1,14 @@
package com.ski.lichuan.admin.controller.monitor;
import com.ski.lichuan.common.exception.BusinessException;
import com.ski.lichuan.model.common.DeviceEnum;
import com.ski.lichuan.model.common.HttpResponseData;
import com.ski.lichuan.model.dashboard.*;
import com.ski.lichuan.model.device.Impl.SnowMachineDevice;
import com.ski.lichuan.model.device.config.SnowMachineDeviceParams;
import com.ski.lichuan.model.device.config.SnowMachineDevicePitchHorizontalParams;
import com.ski.lichuan.services.DeviceDataPollingService;
import com.ski.lichuan.services.SnowMachineDeviceService;
import com.ski.lichuan.services.TrailPositionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -28,6 +31,12 @@ public class SkiResortController {
@Autowired
private TrailPositionService trailPositionService;
@Autowired
private SnowMachineDeviceService snowMachineDeviceService;
@Autowired
private DeviceDataPollingService deviceDataPollingService;
@GetMapping("/")
@Operation(summary = "首页-获取滑雪场雪道和造雪机关联信息")
public HttpResponseData<List<List<TrailPosition>>> getDashboardInfo() {
@@ -40,84 +49,27 @@ public class SkiResortController {
@Operation(summary = "获取雪道坑位的详细信息(造雪机)")
public HttpResponseData<TrailPositionInfo> getPositionInfo(Integer trailPositionId) {
HttpResponseData<TrailPositionInfo> responseData = new HttpResponseData<>();
TrailPositionInfo result = new TrailPositionInfo();
Integer trailId = trailPositionId / 10;
Integer position = trailPositionId % 10;
// 雪机设备
SnowMachineDevice snowMachineDevice = new SnowMachineDevice();
snowMachineDevice.setLora("1234567890123456");
snowMachineDevice.setId(1000000000 + trailPositionId);
snowMachineDevice.setName("造雪机-" + trailPositionId);
snowMachineDevice.setType(DeviceEnum.SNOW_MAKER.getValue());
// 雪道位置信息
TrailPosition trailPosition = new TrailPosition();
trailPosition.setId(trailPositionId);
trailPosition.setTrailId(trailId);
trailPosition.setPosition(position);
trailPosition.setName(trailId + "-" + position);
trailPosition.setSnowMachineStatus(1);
trailPosition.setSnowMachineId(snowMachineDevice.getId());
// 雪道位置状态信息
TrailPositionMonitorInfo trailPositionMonitorInfo = new TrailPositionMonitorInfo();
trailPositionMonitorInfo.setTrailPosition(trailPosition);
trailPositionMonitorInfo.setSnowMakerName(snowMachineDevice.getName());
trailPositionMonitorInfo.setPriority(1);
trailPositionMonitorInfo.setEnvironmentTemperature(-3.5);
trailPositionMonitorInfo.setEnvironmentHumidity(50.0);
trailPositionMonitorInfo.setWetBulbTemperature(0.5);
trailPositionMonitorInfo.setCommunicationStatus(true);
trailPositionMonitorInfo.setIsNormal(true);
trailPositionMonitorInfo.setIsRunning(true);
trailPositionMonitorInfo.setTemperatureConditionSatisfied(true);
trailPositionMonitorInfo.setWindSpeed(5.0);
trailPositionMonitorInfo.setWindDirection(180.0);
// 造雪机设备状态
SnowMachineDeviceInfo snowMachineDeviceInfo = new SnowMachineDeviceInfo();
snowMachineDeviceInfo.setSnowMachineDevice(snowMachineDevice);
snowMachineDeviceInfo.setFrontWaterPressure(0.5 + Math.random() * 0.3); // 前端水压 0.5-0.8 MPa
snowMachineDeviceInfo.setBackWaterPressure(0.4 + Math.random() * 0.2); // 后端水压 0.4-0.6 MPa
snowMachineDeviceInfo.setPressure(0.8 + Math.random() * 0.2); // 气压 0.8-1.0 MPa
snowMachineDeviceInfo.setTemperature(2.5 + Math.random() * 2.0); // 水温 2.5-4.5 ℃
snowMachineDeviceInfo.setGivenAngle(45.0 + Math.random() * 10.0); // 给定开度 45-55°
snowMachineDeviceInfo.setActualAngle(44.0 + Math.random() * 12.0); // 实际开度 44-56°
snowMachineDeviceInfo.setIsNormal(Math.random() > 0.1); // 是否正常 90%概率正常
snowMachineDeviceInfo.setMode((int)(Math.random() * 3) + 1); // 模式 1-3随机
snowMachineDeviceInfo.setCompressorStatus((int)(Math.random() * 2) + 1); // 空压机状态 1-2随机
snowMachineDeviceInfo.setFanStatus((int)(Math.random() * 2) + 1); // 风机状态 1-2随机
snowMachineDeviceInfo.setHeatingRingStatus((int)(Math.random() * 2) + 1); // 加热环状态 1-2随机
snowMachineDeviceInfo.setValveStatus((int)(Math.random() * 2) + 1); // 电磁阀状态1 1-2随机
snowMachineDeviceInfo.setValveStatus2((int)(Math.random() * 2) + 1); // 电磁阀状态2 1-2随机
snowMachineDeviceInfo.setValveStatus3((int)(Math.random() * 2) + 1); // 电磁阀状态3 1-2随机
snowMachineDeviceInfo.setPitchMode((int)(Math.random() * 3) + 1); // 俯仰模式 1-3随机
snowMachineDeviceInfo.setPitchAngle(15.0 + Math.random() * 20.0); // 俯仰角度 15-35°
snowMachineDeviceInfo.setPitchStatus((int)(Math.random() * 2) + 1); // 俯仰状态 1-2随机
snowMachineDeviceInfo.setYawMode((int)(Math.random() * 3) + 1); // 摆头模式 1-3随机
snowMachineDeviceInfo.setYawAngle(30.0 + Math.random() * 40.0); // 摆头角度 30-70°
snowMachineDeviceInfo.setYawStatus((int)(Math.random() * 2) + 1); // 摆头状态 1-2随机
// 造雪机配置
SnowMachineDeviceParams snowMakerDeviceParams = new SnowMachineDeviceParams();
snowMakerDeviceParams.setWorkMode((int)(Math.random() * 3) + 1); // 工作模式 1-3随机
snowMakerDeviceParams.setPriority((int)(Math.random() * 5) + 1); // 优先级 1-5随机
snowMakerDeviceParams.setSnowQuality((int)(Math.random() * 5) + 1); // 雪质 1-5随机
snowMakerDeviceParams.setStartTemperature(-5.0 + Math.random() * 10.0); // 启动温度 -5.0 ~ 5.0
snowMakerDeviceParams.setStopTemperature(-5.0 + Math.random() * 10.0); // 停止温度 -5.0 ~ 5.0
snowMakerDeviceParams.setFlowAdjustment((int)(Math.random() * 50)); // 流量调节 0 ~ 50
// 数据统计
TrailPositionStatisticsInfo trailPositionStatisticsInfo = new TrailPositionStatisticsInfo();
trailPositionStatisticsInfo.setRunningDuration(120.5 + Math.random() * 100.0); // 运行时长 120.5-220.5分钟
trailPositionStatisticsInfo.setFlowStatistics(1000.0 + Math.random() * 500.0); // 流量统计 1000-1500 L/H
trailPositionStatisticsInfo.setSnowArea(500.0 + Math.random() * 300.0); // 造雪面积 500-800 平方米
result.setTrailPositionMonitorInfo(trailPositionMonitorInfo);
result.setSnowMakerInfo(snowMachineDeviceInfo);
result.setSnowMakerDeviceParams(snowMakerDeviceParams);
result.setTrailPositionStatisticsInfo(trailPositionStatisticsInfo);
return responseData.success(result);
TrailPosition trailPosition = trailPositionService.getTrailPosition(trailPositionId);
if (trailPosition == null) {
throw new BusinessException("雪道坑位不存在");
}
if (trailPosition.getSnowMachineId() == null) {
throw new BusinessException("雪道坑位未绑定造雪机");
}
SnowMachineDevice snowMachineDevice = snowMachineDeviceService.getSnowMachineDeviceById(trailPosition.getSnowMachineId());
if (snowMachineDevice == null) {
throw new BusinessException("造雪机不存在");
}
if (snowMachineDevice.getLora() == null) {
throw new BusinessException("造雪机未绑定Lora");
}
TrailPositionInfo trailPositionInfo = deviceDataPollingService.getTrailPositionInfo(snowMachineDevice.getLora());
if (trailPositionInfo == null) {
throw new BusinessException("没有造雪机数据");
}
// 加入造雪机优先级列表
deviceDataPollingService.updateSnowmakersDevicePriorityIds(trailPosition.getSnowMachineId());
return responseData.success(trailPositionInfo);
}
@PostMapping("/connectDevice")

View File

@@ -1,5 +1,6 @@
package com.ski.lichuan.mapper;
import com.ski.lichuan.model.dashboard.SnowMachineDeviceInfo;
import com.ski.lichuan.model.device.Impl.SnowMachineDevice;
import org.apache.ibatis.annotations.Mapper;
@@ -27,4 +28,8 @@ public interface SnowMachineMapper {
int updateNameAndLora(SnowMachineDevice snowMachineDevice);
int deleteById(Integer id);
List<SnowMachineDevice> selectByIds(List<Integer> deviceIds);
List<SnowMachineDevice> selectByName(String name);
}

View File

@@ -50,4 +50,6 @@ public class SnowMachineDeviceInfo {
public Double yawAngle;
@Schema(description = "摆头状态 关1/开2")
public Integer yawStatus;
@Schema(description = "故障状态 0正常 1故障")
public Boolean faultStatus;
}

View File

@@ -0,0 +1,26 @@
package com.ski.lichuan.model.dashboard;
import lombok.Data;
@Data
public class SnowMachineWorkingInfo {
// 造雪机名称
private String snowMakerName;
// 雪道位置
private Integer trailPosition;
// 雪道
private String trailName;
// 优先级
private Integer priority;
// 工作模式
private Integer workingMode;
// 通讯
private Boolean communicationStatus;
// 故障
private Boolean faultStatus;
// 运行
private Boolean runningStatus;
// 温度条件
private Boolean temperatureConditionSatisfied;
}

View File

@@ -58,4 +58,17 @@
<insert id="insertById" parameterType="com.ski.lichuan.model.device.Impl.SnowMachineDevice">
INSERT INTO snow_machine (id, name, lora) VALUES (#{id}, #{name}, #{lora})
</insert>
<!-- 根据ID列表查询造雪机设备 -->
<select id="selectByIds" parameterType="java.util.List" resultMap="SnowMachineResultMap">
SELECT * FROM snow_machine WHERE id IN
<foreach collection="deviceIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<!-- 根据名称查询造雪机设备 -->
<select id="selectByName" parameterType="java.lang.String" resultMap="SnowMachineResultMap">
SELECT * FROM snow_machine WHERE name LIKE '%' || #{name} || '%'
</select>
</mapper>

View File

@@ -1,5 +1,9 @@
package com.ski.lichuan;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.thingsboard.rest.client.RestClient;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
@@ -9,22 +13,23 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<Device> devicesList = getDevices();
sendQueryCommand();
// List<Device> devicesList = getDevices();
// List<DashboardInfo> dashboardsList = getDashboards();
for (Device device : devicesList) {
if (!device.getName().equals("iot_snowmaker_m005")) {
continue;
}
System.out.println("找到指定设备");
getDataByDevice(device);
}
// for (Device device : devicesList) {
// if (!device.getName().equals("iot_snowmaker_m005")) {
// continue;
// }
// System.out.println("找到指定设备");
// getDataByDevice(device);
// }
}
/**
@@ -116,4 +121,118 @@ public class Demo {
}
/**
* 下发查询指令
*/
public static void sendQueryCommand() {
String url = "https://tb.ski.bkiiot.com";
String username = "tenant@thingsboard.org";
String password = "tenant";
RestClient client = new RestClient(url);
client.login(username, password);
List<Device> snowmakersDevices = new ArrayList<>();
List<Integer> snowmakersDeviceIds = new ArrayList<>();
Device snowmakersDevice = null;
for (Device device : getDevices()) {
if (device.getName().equals("snowmakers")) {
System.out.println("找到SNOWMAKERS");
snowmakersDevice = device;
} else if (device.getLabel() != null && device.getLabel().startsWith("s-")) {
System.out.println("找到雪机设备" + device.getLabel());
snowmakersDevices.add(device);
String label = device.getLabel();
String[] labelElement = label.split("-");
if (labelElement.length > 1) {
try {
int deviceId = Integer.parseInt(labelElement[1]);
snowmakersDeviceIds.add(deviceId);
} catch (NumberFormatException e) {
System.out.println("雪机设备ID格式错误" + labelElement[1]);
}
} else {
System.out.println("雪机设备标签格式错误" + device.getLabel());
}
}
}
// JSON: {"id": 10, "method": "query", "params":[PARAMS]}
JsonObject data = new JsonObject();
data.addProperty("method", "query");
data.addProperty("id", "10");
JsonArray paramsArray = new JsonArray();
data.add("params", paramsArray);
for (int deviceId : snowmakersDeviceIds) {
paramsArray.add(deviceId);
}
try {
// 打印JSON字符串
System.out.println(snowmakersDevice.getId());
System.out.println(data.toString());
JsonNode jsonNode = new ObjectMapper().readTree(data.toString());
client.handleOneWayDeviceRPCRequest(snowmakersDevice.getId(), jsonNode);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 下发设备配置指令
*/
public static void sendConfigCommand() {
String url = "https://tb.ski.bkiiot.com";
String username = "tenant@thingsboard.org";
String password = "tenant";
RestClient client = new RestClient(url);
client.login(username, password);
List<Device> snowmakersDevices = new ArrayList<>();
List<Integer> snowmakersDeviceIds = new ArrayList<>();
Device snowmakersDevice = null;
for (Device device : getDevices()) {
if (device.getName().equals("snowmakers")) {
System.out.println("找到SNOWMAKERS");
snowmakersDevice = device;
} else if (device.getLabel() != null && device.getLabel().startsWith("s-")) {
System.out.println("找到雪机设备" + device.getLabel());
snowmakersDevices.add(device);
String label = device.getLabel();
String[] labelElement = label.split("-");
if (labelElement.length > 1) {
try {
int deviceId = Integer.parseInt(labelElement[1]);
snowmakersDeviceIds.add(deviceId);
} catch (NumberFormatException e) {
System.out.println("雪机设备ID格式错误" + labelElement[1]);
}
} else {
System.out.println("雪机设备标签格式错误" + device.getLabel());
}
}
}
// JSON: {"id": 10, "method": "query", "params":[PARAMS]}
JsonObject data = new JsonObject();
data.addProperty("method", "query");
data.addProperty("id", "10");
JsonArray paramsArray = new JsonArray();
data.add("params", paramsArray);
for (int deviceId : snowmakersDeviceIds) {
paramsArray.add(deviceId);
}
try {
// 打印JSON字符串
System.out.println(snowmakersDevice.getId());
System.out.println(data.toString());
JsonNode jsonNode = new ObjectMapper().readTree(data.toString());
client.handleOneWayDeviceRPCRequest(snowmakersDevice.getId(), jsonNode);
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -1,6 +1,15 @@
package com.ski.lichuan.services;
import com.ski.lichuan.model.dashboard.TrailPosition;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.ski.lichuan.mapper.SnowMachineMapper;
import com.ski.lichuan.mapper.TrailPositionMapper;
import com.ski.lichuan.model.dashboard.*;
import com.ski.lichuan.model.device.Impl.SnowMachineDevice;
import com.ski.lichuan.model.device.config.SnowMachineDeviceParams;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -8,15 +17,20 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.rest.client.RestClient;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import static java.lang.Boolean.parseBoolean;
import static java.lang.Double.parseDouble;
import static java.lang.Integer.parseInt;
@Slf4j
@Service
public class DeviceDataPollingService {
@@ -31,37 +45,35 @@ public class DeviceDataPollingService {
private String clientUsername;
@Value("${thingsboard.client.password:tenant}")
private String clientPassword;
// 客户端
private RestClient client;
// 雪机设备状态 - Lora
private final Map<String, TrailPositionInfo> trailPositionInfoMap = new HashMap<>();
// 雪机设备状态 - TrailPosition
private final Map<Integer, TrailPositionInfo> trailPositionInfoMapByTrailPosition = new HashMap<>();
@Getter
private Device snowmakersDevice;
// 设备优先级列表(造雪机)
private final List<String> snowmakersDevicePriorityIds = new ArrayList<>();
@Autowired
private TrailPositionService trailPositionService;
private TrailPositionMapper trailPositionMapper;
@Autowired
private SnowMachineDeviceService snowMachineDeviceService;
private SnowMachineMapper snowMachineMapper;
/**
* 定时轮询设备状态
*/
@Scheduled(fixedRate = 60000)
public void pollDeviceStatus() {
/**
* 核心逻辑每60秒轮询一次所有设备状态查询所有坑位上的雪机和水泵的状态
*/
public TrailPositionInfo getTrailPositionInfo(String lora) {
return trailPositionInfoMap.get(lora);
}
/**
* 定时轮询设备数据
* 每15秒执行一次
*/
@Scheduled(fixedRate = 15000)
public void pollDeviceData() {
/**
* 核心逻辑每15秒轮询一次所有设备数据查询所有坑位上的雪机和水泵的状态
*/
public TrailPositionInfo getTrailPositionInfo(TrailPosition trailPosition) {
if (trailPosition == null) {
return null;
}
return trailPositionInfoMapByTrailPosition.get(trailPosition.getId());
}
/**
@@ -76,7 +88,7 @@ public class DeviceDataPollingService {
log.info("开始查询设备列表");
List<Device> devices = getDevices();
List<TrailPosition> trailPositions = trailPositionService.getAllTrailPositionList();
List<TrailPosition> trailPositions = trailPositionMapper.selectAll();
// 转为Map方便查询 (TrailId,Position) -> TrailPosition
Map<Integer, TrailPosition> trailPositionMap = trailPositions.stream()
.collect(Collectors.toMap(trailPosition -> trailPosition.getTrailId() * 10000 + trailPosition.getPosition(), trailPosition -> trailPosition));
@@ -112,46 +124,393 @@ public class DeviceDataPollingService {
log.warn("造雪机设备 {} 标签格式错误,跳过", device.getLabel());
continue;
}
Integer idValue = Integer.parseInt(id);
Integer trailIdValue = Integer.parseInt(trailId);
Integer positionValue = Integer.parseInt(position);
// 更新雪机信息
String name = device.getName() != null ? device.getName() : "雪机" + trailId + position;
try {
snowMachineDeviceService.updateSnowMachineDeviceNameAndLora(idValue, name, device.getId().toString());
Integer idValue = Integer.parseInt(id);
Integer trailIdValue = Integer.parseInt(trailId);
Integer positionValue = Integer.parseInt(position);
// 更新雪机信息
String name = device.getName() != null ? device.getName() : "雪机" + trailId + position;
String lora = device.getId().toString();
if (snowMachineMapper.selectById(idValue) == null) {
// 则新增
SnowMachineDevice newSnowMachineDevice = new SnowMachineDevice();
newSnowMachineDevice.setId(idValue);
newSnowMachineDevice.setName(name);
newSnowMachineDevice.setLora(lora);
snowMachineMapper.insertById(newSnowMachineDevice);
} else {
// 更新造雪机设备信息
SnowMachineDevice snowMachineDevice = snowMachineMapper.selectById(idValue);
snowMachineDevice.setName(name);
snowMachineDevice.setLora(lora);
snowMachineMapper.updateName(snowMachineDevice);
}
log.info("更新造雪机设备信息 {} 成功", device.getLabel());
// 检查是否存在对应的雪道位置
TrailPosition trailPosition = trailPositionMap.get(trailIdValue * 10000 + positionValue);
if (trailPosition == null) {
// 雪道位置不存在,跳过
log.warn("造雪机设备 {} 对应的雪道位置不存在,跳过", device.getLabel());
continue;
}
// 更新雪道位置的造雪机ID
trailPosition.setSnowMachineId(idValue);
TrailPosition trailPositionObj = trailPositionMapper.selectById(trailPosition.getId());
if (trailPositionObj == null) {
// 雪道位置不存在,跳过
log.warn("造雪机设备 {} 对应的雪道位置不存在,跳过", device.getLabel());
continue;
}
trailPositionObj.setSnowMachineId(idValue);
trailPositionMapper.update(trailPositionObj);
// 从Map中删除该雪道位置防止重复处理
trailPositionMap.remove(trailIdValue * 10000 + positionValue);
} catch (NumberFormatException e) {
// 转换失败,跳过
log.warn("造雪机设备 {} 标签格式错误,跳过", device.getLabel());
continue;
} catch (Exception e) {
log.error("更新造雪机设备信息 {} 失败:{}", device.getLabel(), e.getMessage());
}
// 检查是否存在对应的雪道位置
TrailPosition trailPosition = trailPositionMap.get(trailIdValue * 10000 + positionValue);
if (trailPosition == null) {
// 雪道位置不存在,跳过
log.warn("造雪机设备 {} 对应的雪道位置不存在,跳过", device.getLabel());
continue;
}
// 更新雪道位置的造雪机ID
trailPosition.setSnowMachineId(idValue);
trailPositionService.updateTrailPositionSnowMachineId(trailPosition.getId(), idValue);
// 从Map中删除该雪道位置防止重复处理
trailPositionMap.remove(trailIdValue * 10000 + positionValue);
} else if (device.getName() != null && device.getName().equals("snowmakers")) {
snowmakersDevice = device;
}
}
// 清楚Map中剩余的雪道位置
List<Integer> mapIdList = trailPositionMap.values().stream().map(TrailPosition::getId).collect(Collectors.toList());
try {
trailPositionService.clearTrailPositionSnowMachineIdBatch(mapIdList);
// 清空雪道位置雪机信息
for (Integer trailPositionId : mapIdList) {
TrailPosition trailPosition = trailPositionMapper.selectById(trailPositionId);
if (trailPosition == null) {
continue;
}
trailPosition.setSnowMachineId(null);
trailPosition.setSnowMachineStatus(0);
trailPositionMapper.clearSnowMachine(trailPositionId);
}
} catch (Exception e) {
log.error("清除雪道位置 {} 造雪机ID失败{}", mapIdList, e.getMessage());
}
}
/**
* 发出采数指令
*/
@Scheduled(fixedRate = 6000)
public boolean sendQueryCommand() {
try {
RestClient client = getClient();
List<Integer> queryIds = trailPositionMapper.selectAll().stream()
.filter(trailPosition -> trailPosition.getSnowMachineId() != null)
.map(TrailPosition::getSnowMachineId)
.collect(Collectors.toList());
// 如果snowmakersDevicePriorityIds不为空需要前置处理
log.info("原始顺序: {}", queryIds);
if (!snowmakersDevicePriorityIds.isEmpty()) {
// 先处理snowmakersDevicePriorityIds中的设备
for (String deviceId : snowmakersDevicePriorityIds) {
if (queryIds.contains(Integer.parseInt(deviceId))) {
// 移除原来的位置
int index = queryIds.indexOf(Integer.parseInt(deviceId));
if (index != -1) {
queryIds.remove(index);
}
// 加入到开头
queryIds.addFirst(Integer.parseInt(deviceId));
}
}
}
log.info("处理后的顺序: {}", queryIds);
JsonObject data = new JsonObject();
data.addProperty("method", "query");
data.addProperty("id", "10");
JsonArray paramsArray = new JsonArray();
data.add("params", paramsArray);
for (int deviceId : queryIds) {
paramsArray.add(deviceId);
}
try {
// 打印JSON字符串
log.info("snowmakersDeviceId: {}", snowmakersDevice.getId());
log.info("queryIds: {}", queryIds);
JsonNode jsonNode = new ObjectMapper().readTree(data.toString());
client.handleOneWayDeviceRPCRequest(snowmakersDevice.getId(), jsonNode);
return true;
} catch (IOException e) {
log.error("下发造雪机设备取数失败:{}", e.getMessage());
e.printStackTrace();
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 定时轮询设备数据
* 每15秒执行一次
*/
@Scheduled(fixedRate = 15000)
public void pollDeviceData() {
/**
* 核心逻辑每15秒轮询一次所有设备数据查询所有坑位上的雪机和水泵的状态
*/
log.info("开始查询设备数据");
// 开始时间戳
long startTime = System.currentTimeMillis();
RestClient client = getClient();
// 获取所有设备
List<TrailPosition> trailPositions = trailPositionMapper.selectAll();
List<Integer> deviceIds = trailPositions.stream()
.map(TrailPosition::getSnowMachineId)
.filter(id -> id != null)
.collect(Collectors.toList());
// 获得DeviceId 与 TrailPosition 映射关系
Map<String, TrailPosition> trailPositionByDeviceIdMap = new HashMap<>();
for (TrailPosition trailPosition : trailPositions) {
if (trailPosition.getSnowMachineId() != null) {
trailPositionByDeviceIdMap.put(trailPosition.getSnowMachineId().toString(), trailPosition);
}
}
List<SnowMachineDevice> devices = snowMachineMapper.selectByIds(deviceIds);
if (devices.isEmpty()) {
log.warn("没有查询到造雪机设备");
return;
}
// 清空旧数据
// trailPositionInfoMap.clear();
for (SnowMachineDevice device : devices) {
try {
if (device.getLora() != null) {
EntityId deviceId = new DeviceId(UUID.fromString(device.getLora()));
// 获取设备的最新时间序列数据
List<TsKvEntry> timeseriesData = client.getLatestTimeseries(deviceId, List.of());
// 确保有数据
if (timeseriesData != null && !timeseriesData.isEmpty()) {
// 处理时间序列数据并填充到deviceInfo中
TrailPositionInfo trailPositionInfo = processTimeseriesData(timeseriesData);
trailPositionInfo.getTrailPositionMonitorInfo().setHasSnowMaker(true);
trailPositionInfo.getSnowMakerInfo().setSnowMachineDevice(device);
trailPositionInfoMap.put(device.getLora(), trailPositionInfo);
// 更新雪道位置的状态
TrailPosition trailPosition = trailPositionByDeviceIdMap.get(device.getId().toString());
if (trailPosition != null) {
trailPositionInfoMapByTrailPosition.put(trailPosition.getId(), trailPositionInfo);
}
} else {
log.warn("设备 {} 没有返回时间序列数据", device.getName());
}
}
} catch (Exception e) {
log.error("处理设备 {} 数据时发生错误:{}", device.getName(), e.getMessage(), e);
}
}
// 结束时间戳
long endTime = System.currentTimeMillis();
// 计算查询耗时
long queryDuration = endTime - startTime;
log.info("查询设备数据耗时: {} 毫秒", queryDuration);
}
/**
* 处理时间序列数据并填充到SnowMachineDeviceInfo对象中
* @param timeseriesData 时间序列数据
*/
private TrailPositionInfo processTimeseriesData(List<TsKvEntry> timeseriesData) {
TrailPositionInfo trailPositionInfo = new TrailPositionInfo();
TrailPositionMonitorInfo trailPositionMonitorInfo = new TrailPositionMonitorInfo();
TrailPositionStatisticsInfo trailPositionStatisticsInfo = new TrailPositionStatisticsInfo();
SnowMachineDeviceParams snowMachineDeviceParams = new SnowMachineDeviceParams();
SnowMachineDeviceInfo snowMachineDeviceInfo = new SnowMachineDeviceInfo();
// 遍历所有时间序列键值对
for (TsKvEntry entry : timeseriesData.stream().filter(e -> e.getValue() != null).toList()) {
String key = entry.getKey();
String stringValue = entry.getValueAsString();
// 确保有数据
if (stringValue != null && !stringValue.isEmpty()) {
switch (key) {
case "running":
Boolean running = parseBoolean(stringValue);
snowMachineDeviceInfo.setIsNormal(running);
trailPositionMonitorInfo.setIsNormal(running);
if (!running) {
snowMachineDeviceInfo.setMode(1);
trailPositionMonitorInfo.setAutoMode(false);
snowMachineDeviceParams.setWorkMode(1);
}
break;
case "autoMode":
Boolean autoMode = parseBoolean(stringValue);
trailPositionMonitorInfo.setAutoMode(autoMode);
if (snowMachineDeviceInfo.getMode() == null) {
if (autoMode) {
snowMachineDeviceInfo.setMode(3);
} else {
snowMachineDeviceInfo.setMode(2);
}
}
break;
case "ambientTemperature":
trailPositionMonitorInfo.setEnvironmentTemperature(parseDouble(stringValue));
break;
case "ambientHumidity":
trailPositionMonitorInfo.setEnvironmentHumidity(parseDouble(stringValue));
break;
case "wetBulbTemperature":
trailPositionMonitorInfo.setWetBulbTemperature(parseDouble(stringValue));
break;
case "startupTemperature1":
// 转换
snowMachineDeviceParams.setStartTemperature(parseDouble(stringValue));
break;
case "shutdownTemperature":
// 转换
snowMachineDeviceParams.setStopTemperature(parseDouble(stringValue));
break;
case "snowQualitySetting":
// 转换
snowMachineDeviceParams.setSnowQuality(parseInt(stringValue));
break;
case "realTimeFlowRate":
// 转换
snowMachineDeviceParams.setFlowAdjustment(parseInt(stringValue));
break;
case "operatingHoursAccumulated":
// 转换
trailPositionStatisticsInfo.setRunningDuration((trailPositionStatisticsInfo.getRunningDuration() != null ? trailPositionStatisticsInfo.getRunningDuration() : 0.0) + parseDouble(stringValue) * 3600);
break;
case "operatingMinutesAccumulated":
// 转换
trailPositionStatisticsInfo.setRunningDuration((trailPositionStatisticsInfo.getRunningDuration() != null ? trailPositionStatisticsInfo.getRunningDuration() : 0.0) + parseDouble(stringValue) * 60);
break;
case "operatingSecondsAccumulated":
// 转换
trailPositionStatisticsInfo.setRunningDuration((trailPositionStatisticsInfo.getRunningDuration() != null ? trailPositionStatisticsInfo.getRunningDuration() : 0.0) + parseDouble(stringValue));
break;
// preFilterWaterPressure=>frontWaterPressure
case "preFilterWaterPressure":
snowMachineDeviceInfo.setFrontWaterPressure(parseDouble(stringValue));
break;
// postFilterWaterPressure=>backWaterPressure
case "postFilterWaterPressure":
snowMachineDeviceInfo.setBackWaterPressure(parseDouble(stringValue));
break;
// airPressure=>pressure
case "airPressure":
snowMachineDeviceInfo.setPressure(parseDouble(stringValue));
break;
// waterTemperature=>temperature
case "waterTemperature":
snowMachineDeviceInfo.setTemperature(parseDouble(stringValue));
break;
// waterValveFeedbackOpening=>actualAngle
case "waterValveFeedbackOpening":
snowMachineDeviceInfo.setActualAngle(parseDouble(stringValue));
break;
// waterValveSetOpening=>givenAngle
case "waterValveSetOpening":
snowMachineDeviceInfo.setGivenAngle(parseDouble(stringValue));
break;
// fanRunning=>fanStatus
case "fanRunning":
snowMachineDeviceInfo.setFanStatus(parseBoolean(stringValue) ? 2 : 1);
break;
// airCompressorRunning=>compressorStatus
case "airCompressorRunning":
snowMachineDeviceInfo.setCompressorStatus(parseBoolean(stringValue) ? 2 : 1);
break;
// heatingRingRunning=>heatingRingStatus
case "heatingRingRunning":
snowMachineDeviceInfo.setHeatingRingStatus(parseBoolean(stringValue) ? 2 : 1);
break;
// ballValve1OpenFeedback=>valveStatus(2)
case "ballValve1OpenFeedback":
if (parseBoolean(stringValue)) {
snowMachineDeviceInfo.setValveStatus(2);
}
break;
// ballValve1CloseFeedback=>valveStatus(1)
case "ballValve1CloseFeedback":
if (parseBoolean(stringValue)) {
snowMachineDeviceInfo.setValveStatus(1);
}
break;
// ballValve2OpenFeedback=>valveStatus2(2)
case "ballValve2OpenFeedback":
if (parseBoolean(stringValue)) {
snowMachineDeviceInfo.setValveStatus2(2);
}
break;
// ballValve2CloseFeedback=>valveStatus2(1)
case "ballValve2CloseFeedback":
if (parseBoolean(stringValue)) {
snowMachineDeviceInfo.setValveStatus2(1);
}
break;
// ballValve3OpenFeedback=>valveStatus3(2)
case "ballValve3OpenFeedback":
if (parseBoolean(stringValue)) {
snowMachineDeviceInfo.setValveStatus3(2);
}
break;
// ballValve3CloseFeedback=>valveStatus3(1)
case "ballValve3CloseFeedback":
if (parseBoolean(stringValue)) {
snowMachineDeviceInfo.setValveStatus3(1);
}
break;
// panningAuto=>yawMode(3)
case "panningAuto":
snowMachineDeviceInfo.setYawMode(parseBoolean(stringValue) ? 3 : 1);
break;
// airDuctAuto=>pitchMode(3)
case "airDuctAuto":
snowMachineDeviceInfo.setPitchMode(parseBoolean(stringValue) ? 3 : 1);
break;
// fault
case "fault":
snowMachineDeviceInfo.setFaultStatus(parseBoolean(stringValue));
break;
default:
break;
}
}
}
trailPositionInfo.setSnowMakerInfo(snowMachineDeviceInfo);
trailPositionInfo.setSnowMakerDeviceParams(snowMachineDeviceParams);
trailPositionInfo.setTrailPositionStatisticsInfo(trailPositionStatisticsInfo);
trailPositionInfo.setTrailPositionMonitorInfo(trailPositionMonitorInfo);
return trailPositionInfo;
}
private RestClient getClient() {
if (client == null) {
client = new RestClient(clientUrl);
@@ -174,4 +533,16 @@ public class DeviceDataPollingService {
} while (devices.hasNext());
return devicesList;
}
public void updateSnowmakersDevicePriorityIds(Integer id) {
// 如果有则前置
if (id == null) {
return;
}
if (!snowmakersDevicePriorityIds.isEmpty() && snowmakersDevicePriorityIds.contains(id.toString())) {
// 如果存在先移除
snowmakersDevicePriorityIds.removeIf(item -> item.equals(id.toString()));
}
snowmakersDevicePriorityIds.addLast(id.toString());
}
}

View File

@@ -0,0 +1,88 @@
package com.ski.lichuan.services;
import com.ski.lichuan.model.dashboard.SnowMachineWorkingInfo;
import com.ski.lichuan.model.dashboard.TrailPosition;
import com.ski.lichuan.model.dashboard.TrailPositionInfo;
import com.ski.lichuan.model.device.Impl.SnowMachineDevice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Service
public class DeviceMonitorService {
@Autowired
private SnowMachineDeviceService snowMachineDeviceService;
@Autowired
private TrailPositionService trailPositionService;
@Autowired
private DeviceDataPollingService deviceDataPollingService;
/**
* 获取设备监控信息列表,支持按名称,雪道,运营线查询
* @return 设备监控信息
*/
public List<SnowMachineWorkingInfo> getDeviceMonitorList(String name, Integer trailId, Integer priority) {
List<SnowMachineDevice> snowMachineDevices = snowMachineDeviceService.getDeviceMonitorList(name);
List<TrailPosition> trailPositions = trailPositionService.getAllTrailPositionList();
// 将trailPositions转换为Mapkey为trailIdvalue为TrailPosition列表
Map<Integer, List<TrailPosition>> trailPositionMap = trailPositions.stream()
.collect(Collectors.groupingBy(TrailPosition::getTrailId));
// 将trailPositions转为以snowMachineId为keyTrailPosition列表为value的Map若snowMachineId为null则不包含在Map中
Map<Integer, List<TrailPosition>> snowMachineTrailPositionMap = trailPositions.stream()
.filter(trailPosition -> trailPosition.getSnowMachineId() != null)
.collect(Collectors.groupingBy(TrailPosition::getSnowMachineId));
List<SnowMachineWorkingInfo> snowMachineWorkingInfos = snowMachineDevices.stream().map(snowMachineDevice -> {
// 基础信息
SnowMachineWorkingInfo snowMachineWorkingInfo = new SnowMachineWorkingInfo();
snowMachineWorkingInfo.setSnowMakerName(snowMachineDevice.getName());
snowMachineWorkingInfo.setPriority(1);
// 存在雪道位置信息
if (snowMachineTrailPositionMap.containsKey(snowMachineDevice.getId())) {
TrailPosition trailPosition = snowMachineTrailPositionMap.get(snowMachineDevice.getId()).get(0);
snowMachineWorkingInfo.setTrailPosition(trailPosition.getTrailId());
snowMachineWorkingInfo.setTrailName(trailPositionMap.get(trailPosition.getTrailId()).get(0).getName());
}
// 根据Lora查询设备详细信息
if (snowMachineDevice.getLora() != null) {
TrailPositionInfo trailPositionInfo = deviceDataPollingService.getTrailPositionInfo(snowMachineDevice.getLora());
if (trailPositionInfo != null) {
snowMachineWorkingInfo.setWorkingMode(trailPositionInfo.getSnowMakerInfo().getMode());
snowMachineWorkingInfo.setCommunicationStatus(trailPositionInfo.getSnowMakerInfo().getIsNormal());
snowMachineWorkingInfo.setFaultStatus(trailPositionInfo.getSnowMakerInfo().getFaultStatus());
snowMachineWorkingInfo.setRunningStatus(trailPositionInfo.getSnowMakerInfo().getIsNormal());
snowMachineWorkingInfo.setTemperatureConditionSatisfied(trailPositionInfo.getSnowMakerInfo().getIsNormal());
}
}
return snowMachineWorkingInfo;
}).collect(Collectors.toList());
// 过滤雪道
if (trailId != null) {
snowMachineWorkingInfos = snowMachineWorkingInfos.stream()
.filter(info -> info.getTrailPosition() != null && info.getTrailPosition().equals(trailId))
.collect(Collectors.toList());
}
// 过滤优先级
if (priority != null) {
snowMachineWorkingInfos = snowMachineWorkingInfos.stream()
.filter(info -> info.getPriority() != null && info.getPriority().equals(priority))
.collect(Collectors.toList());
}
return snowMachineWorkingInfos;
}
}

View File

@@ -1,4 +0,0 @@
package com.ski.lichuan.services;
public class IoTService {
}

View File

@@ -1,6 +1,7 @@
package com.ski.lichuan.services;
import com.ski.lichuan.mapper.SnowMachineMapper;
import com.ski.lichuan.model.dashboard.SnowMachineDeviceInfo;
import com.ski.lichuan.model.device.Impl.SnowMachineDevice;
import com.ski.lichuan.model.device.config.SnowMachineDeviceParams;
import org.springframework.beans.factory.annotation.Autowired;
@@ -14,7 +15,20 @@ public class SnowMachineDeviceService {
@Autowired
private SnowMachineMapper snowMachineMapper;
/**
/**
* 查询所有造雪机设备监控信息
*
* @return 造雪机设备监控信息列表
*/
public List<SnowMachineDevice> getDeviceMonitorList(String name) {
if (name == null || name.isEmpty()) {
return snowMachineMapper.selectAll();
} else {
return snowMachineMapper.selectByName(name);
}
}
/**
* 新增造雪机设备
*
* @param snowMachineDevice 造雪机设备
@@ -43,8 +57,12 @@ public class SnowMachineDeviceService {
*
* @return 所有造雪机设备列表
*/
public List<SnowMachineDevice> getAllSnowMachineDevices() {
return snowMachineMapper.selectAll();
public List<SnowMachineDevice> getAllSnowMachineDevices(String keyword) {
if (keyword == null || keyword.isEmpty()) {
return snowMachineMapper.selectAll();
} else {
return snowMachineMapper.selectByName(keyword);
}
}
/**
@@ -155,4 +173,14 @@ public class SnowMachineDeviceService {
}
return true;
}
/**
* 根据ID列表查询造雪机设备
*
* @param deviceIds 造雪机设备ID列表
* @return 造雪机设备列表
*/
public List<SnowMachineDevice> getDevicesByIds(List<Integer> deviceIds) {
return snowMachineMapper.selectByIds(deviceIds);
}
}

View File

@@ -23,6 +23,11 @@ public class TrailPositionService {
@Autowired
private SnowMachineMapper snowMachineDeviceMapper;
@Autowired
private DeviceDataPollingService deviceDataPollingService;
/**
* 获取所有已绑定雪机的雪道位置信息
*/
@@ -54,9 +59,9 @@ public class TrailPositionService {
for (List<TrailPosition> trailPositions : result) {
// 更新trailPosition状态
for (TrailPosition trailPosition : trailPositions) {
if (trailPosition.getSnowMachineStatus() == 0) {
// 暂时随机
trailPosition.setSnowMachineStatus((int) (Math.random() * 3));
trailPosition.setSnowMachineStatus(0);
if (deviceDataPollingService.getTrailPositionInfo(trailPosition) != null) {
trailPosition.setSnowMachineStatus(deviceDataPollingService.getTrailPositionInfo(trailPosition).getSnowMakerInfo().getIsNormal() ? 1 : 2);
}
}
@@ -66,6 +71,10 @@ public class TrailPositionService {
return result;
}
public TrailPosition getTrailPosition(Integer id) {
return trailPositionMapper.selectById(id);
}
/**
* 获取雪道位置的详细信息(造雪机)
*/
@@ -74,6 +83,9 @@ public class TrailPositionService {
if (trailPosition == null) {
return null;
} else {
TrailPositionInfo result = new TrailPositionInfo();
Integer trailId = id / 10;