|
@ -1,23 +1,31 @@ |
|
|
package com.zc.business.service.impl; |
|
|
package com.zc.business.service.impl; |
|
|
|
|
|
|
|
|
import cn.hutool.core.date.DateUtil; |
|
|
import cn.hutool.core.date.DateUtil; |
|
|
|
|
|
import com.alibaba.fastjson.JSONArray; |
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|
|
|
|
|
import com.ruoyi.common.core.redis.RedisCache; |
|
|
|
|
|
import com.zc.business.constant.RedisKeyConstants; |
|
|
|
|
|
import com.zc.business.constant.StatisticalRecoveryOffsetTime; |
|
|
|
|
|
import com.zc.business.controller.DcDeviceController; |
|
|
|
|
|
import com.zc.business.domain.DcDevice; |
|
|
import com.zc.business.domain.DcTrafficSectionData; |
|
|
import com.zc.business.domain.DcTrafficSectionData; |
|
|
import com.zc.business.enums.TrafficDataPeriodTypeEnum; |
|
|
import com.zc.business.enums.*; |
|
|
import com.zc.business.statistics.cache.*; |
|
|
import com.zc.business.statistics.cache.*; |
|
|
import com.zc.business.mapper.DcTrafficSectionDataMapper; |
|
|
import com.zc.business.mapper.DcTrafficSectionDataMapper; |
|
|
import com.zc.business.service.DcTrafficSectionDataService; |
|
|
import com.zc.business.service.DcTrafficSectionDataService; |
|
|
import com.zc.business.statistics.handler.RealtimeTrafficStatistics; |
|
|
import com.zc.business.statistics.handler.RealtimeTrafficStatistics; |
|
|
|
|
|
import com.zc.common.core.httpclient.exception.HttpException; |
|
|
|
|
|
import org.slf4j.Logger; |
|
|
|
|
|
import org.slf4j.LoggerFactory; |
|
|
import org.springframework.scheduling.annotation.Scheduled; |
|
|
import org.springframework.scheduling.annotation.Scheduled; |
|
|
import org.springframework.stereotype.Service; |
|
|
import org.springframework.stereotype.Service; |
|
|
|
|
|
|
|
|
import javax.annotation.PostConstruct; |
|
|
import javax.annotation.PostConstruct; |
|
|
import javax.annotation.Resource; |
|
|
import javax.annotation.Resource; |
|
|
import java.util.Date; |
|
|
import java.io.IOException; |
|
|
import java.util.List; |
|
|
import java.util.*; |
|
|
import java.util.Map; |
|
|
|
|
|
import java.util.function.Consumer; |
|
|
import java.util.function.Consumer; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@ -30,21 +38,197 @@ public class DcTrafficSectionDataServiceImpl |
|
|
extends ServiceImpl<DcTrafficSectionDataMapper, DcTrafficSectionData> |
|
|
extends ServiceImpl<DcTrafficSectionDataMapper, DcTrafficSectionData> |
|
|
implements DcTrafficSectionDataService { |
|
|
implements DcTrafficSectionDataService { |
|
|
|
|
|
|
|
|
|
|
|
// 日志记录器
|
|
|
|
|
|
protected final Logger logger = LoggerFactory.getLogger(this.getClass()); |
|
|
|
|
|
|
|
|
@Resource |
|
|
@Resource |
|
|
private DcTrafficSectionDataMapper dcTrafficSectionDataMapper; |
|
|
private DcTrafficSectionDataMapper dcTrafficSectionDataMapper; |
|
|
|
|
|
|
|
|
|
|
|
@Resource |
|
|
|
|
|
private RedisCache redisCache; |
|
|
|
|
|
|
|
|
|
|
|
@Resource |
|
|
|
|
|
private DcDeviceController dcDeviceController; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 初始化方法,用于在对象创建后恢复各种周期的交通数据缓存。 |
|
|
* 初始化方法,用于在对象创建后恢复各种周期的交通数据缓存。 |
|
|
* 该方法标注了@PostConstruct注解,确保在依赖注入完成后调用。 |
|
|
* 该方法标注了@PostConstruct注解,确保在依赖注入完成后调用。 |
|
|
*/ |
|
|
*/ |
|
|
@PostConstruct |
|
|
@PostConstruct |
|
|
public void init() { |
|
|
public void init() { |
|
|
// TODO 恢复每天交通数据缓存(es获取数据)
|
|
|
recoveryDailyCache(); // 从es中恢复当天交通数据缓存
|
|
|
recoveryMonthlyCache(); // 恢复每月交通数据缓存
|
|
|
recoveryMonthlyCache(); // 恢复每月交通数据缓存
|
|
|
recoveryQuarterlyCache(); // 恢复每季度交通数据缓存
|
|
|
recoveryQuarterlyCache(); // 恢复每季度交通数据缓存
|
|
|
recoveryYearlyCache(); // 恢复每年交通数据缓存
|
|
|
recoveryYearlyCache(); // 恢复每年交通数据缓存
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 处理实时接收到的一类交流站设备消息,并将其转换为交通断面统计数据对象并缓存。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param msg 设备发送的JSON格式实时消息 |
|
|
|
|
|
*/ |
|
|
|
|
|
@Override |
|
|
|
|
|
public void processRealtimeOneStopMessage(JSONObject msg) { |
|
|
|
|
|
|
|
|
|
|
|
// 1. 将设备消息转换为交通断面数据统计定义对象
|
|
|
|
|
|
List<DcTrafficSectionData> dcTrafficSectionDataList = convertToTrafficStatistics(msg, DeviceDataCategory.REAL_TIME); |
|
|
|
|
|
|
|
|
|
|
|
if (dcTrafficSectionDataList != null && !dcTrafficSectionDataList.isEmpty()) { |
|
|
|
|
|
// 2. 将转换后的数据添加到缓存中
|
|
|
|
|
|
dcTrafficSectionDataList.forEach(DailyTrafficStatisticsCache::addCacheData); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 定义每小时第20分钟执行的任务,用于清除过期缓存数据并将缓存中的数据整合后保存至数据库。 |
|
|
|
|
|
*/ |
|
|
|
|
|
// @Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务
|
|
|
|
|
|
public void performHourlyCleanupAndPersist() { |
|
|
|
|
|
// 清除已过期的缓存数据
|
|
|
|
|
|
DailyTrafficStatisticsCache.clearExpiredData(); |
|
|
|
|
|
MonthlyTrafficStatisticsCache.clearExpiredData(); |
|
|
|
|
|
QuarterlyTrafficStatisticsCache.clearExpiredData(); |
|
|
|
|
|
YearlyTrafficStatisticsCache.clearExpiredData(); |
|
|
|
|
|
// 整合缓存数据并保存至数据库
|
|
|
|
|
|
// 将缓存中的数据按日统计后保存至数据库
|
|
|
|
|
|
// 添加月交通断面数据到缓存中
|
|
|
|
|
|
persistAggregatedData(DailyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficStatisticsCache::addCacheData); |
|
|
|
|
|
// 将缓存中的数据按月统计后保存至数据库
|
|
|
|
|
|
// 添加季度交通断面数据到缓存中
|
|
|
|
|
|
persistAggregatedData(MonthlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficStatisticsCache::addCacheData); |
|
|
|
|
|
// 将缓存中的数据按季度统计后保存至数据库
|
|
|
|
|
|
// 添加年交通断面数据到缓存中
|
|
|
|
|
|
persistAggregatedData(QuarterlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficStatisticsCache::addCacheData); |
|
|
|
|
|
// 将缓存中的数据按年统计后保存至数据库
|
|
|
|
|
|
persistAggregatedData(YearlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 恢复每日缓存的函数。 |
|
|
|
|
|
* 该方法尝试从物联平台获取所有设备信息,并对这些信息进行处理。 |
|
|
|
|
|
* 如果获取信息失败或处理过程中发生异常,则记录错误信息。 |
|
|
|
|
|
*/ |
|
|
|
|
|
private void recoveryDailyCache() { |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
// 尝试从指定产品ID获取设备信息
|
|
|
|
|
|
Map<String, Object> oneStopDeviceMap = dcDeviceController.getDeviceByProductId(IotProductEnum.ONE_STOP_PRODUCT.value()); |
|
|
|
|
|
|
|
|
|
|
|
// 检查获取的设备信息是否为空
|
|
|
|
|
|
if (oneStopDeviceMap == null || oneStopDeviceMap.get("data") == null) { |
|
|
|
|
|
logger.error("获取一站式设备数据失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value()); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 将获取的设备信息转换为JSON数组,并遍历处理每个设备的数据
|
|
|
|
|
|
JSONArray deviceJsonArray = JSONArray.parseArray(oneStopDeviceMap.get("data").toString()); |
|
|
|
|
|
deviceJsonArray.forEach(this::processDeviceData); |
|
|
|
|
|
|
|
|
|
|
|
} catch (HttpException | IOException e) { |
|
|
|
|
|
// 记录处理设备数据时发生的异常
|
|
|
|
|
|
logger.error("处理设备数据时发生异常", e); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 处理设备数据的函数。 |
|
|
|
|
|
* 该方法首先将传入的设备对象转换为JSON对象,然后从JSON对象中提取设备ID。 |
|
|
|
|
|
* 如果设备ID有效,则查询设备的实时流量数据,并对获取的设备属性数据进行处理。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param deviceObject 设备对象,需要是一个JSONObject,包含设备信息。 |
|
|
|
|
|
*/ |
|
|
|
|
|
private void processDeviceData(Object deviceObject) { |
|
|
|
|
|
JSONObject deviceJsonObject = (JSONObject)deviceObject; |
|
|
|
|
|
// 提取设备ID
|
|
|
|
|
|
String deviceId = deviceJsonObject.getString("id"); |
|
|
|
|
|
|
|
|
|
|
|
// 检查设备ID的有效性
|
|
|
|
|
|
if (deviceId == null || deviceId.isEmpty()) { |
|
|
|
|
|
logger.error("获取一站式设备id失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value()); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 查询并处理设备属性数据
|
|
|
|
|
|
try { |
|
|
|
|
|
HashMap<String, Object> props = buildPropertiesRequiredParameter(); |
|
|
|
|
|
|
|
|
|
|
|
// 查询设备的实时属性数据
|
|
|
|
|
|
Map<String, Object> deviceProperties = dcDeviceController.queryDeviceProperties(deviceId, IotProductPropertiesEnum.ONE_STOP_PRODUCT_01.getNumber(), props); |
|
|
|
|
|
|
|
|
|
|
|
// 对获取的设备属性数据进行进一步处理
|
|
|
|
|
|
processDeviceProperties(deviceProperties); |
|
|
|
|
|
} catch (HttpException | IOException e) { |
|
|
|
|
|
// 记录处理设备属性数据时发生的异常
|
|
|
|
|
|
logger.error("处理设备属性数据时发生异常,设备id:{}", deviceId, e); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 构建必需的属性请求参数集合。 |
|
|
|
|
|
* 该方法不接受任何参数,返回一个包含特定键值对的HashMap对象。 |
|
|
|
|
|
* 键值对表达了查询条件,其中键是查询条件的结构化字符串表示,值是条件的具体值。 |
|
|
|
|
|
* |
|
|
|
|
|
* @return HashMap<String, Object> 包含查询条件的HashMap对象。 |
|
|
|
|
|
*/ |
|
|
|
|
|
private HashMap<String, Object> buildPropertiesRequiredParameter() { |
|
|
|
|
|
|
|
|
|
|
|
// todo 数据库中查询出最大的时间
|
|
|
|
|
|
|
|
|
|
|
|
HashMap<String, Object> props = new HashMap<>(); |
|
|
|
|
|
// 设置查询条件的键为“timestamp$BTW”,表示时间戳在一定范围内
|
|
|
|
|
|
props.put("terms[0].column", "timestamp$BTW"); |
|
|
|
|
|
ArrayList<String> dateList = new ArrayList<>(); |
|
|
|
|
|
// 添加当前日期的开始和结束时间到列表,用于设定时间范围
|
|
|
|
|
|
dateList.add(DateUtil.beginOfDay(DateUtil.offsetDay(new Date(), StatisticalRecoveryOffsetTime.TRAFFIC_SECTION_DATA_OFFSET_DAY)).toString()); |
|
|
|
|
|
dateList.add(DateUtil.endOfDay(new Date()).toString()); |
|
|
|
|
|
// 将日期列表以逗号分隔并设置为查询条件的值
|
|
|
|
|
|
props.put("terms[0].value", String.join(",", dateList)); |
|
|
|
|
|
props.put("paging", false); |
|
|
|
|
|
return props; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 处理设备属性信息。 |
|
|
|
|
|
* 该方法接收一个设备属性的映射,从中提取属性数据,并将其转换为交通断面数据统计定义对象列表,然后将这些数据添加到缓存中。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param deviceProperties 包含设备属性数据的映射。该映射应包含一个名为"data"的键,其值是一个包含具体属性信息的JSON对象。 |
|
|
|
|
|
*/ |
|
|
|
|
|
private void processDeviceProperties(Map<String, Object> deviceProperties) { |
|
|
|
|
|
// 检查传入的设备属性映射是否为空,或者其中的"data"键对应的值是否为空
|
|
|
|
|
|
if (deviceProperties == null || deviceProperties.get("data") == null) { |
|
|
|
|
|
logger.error("获取一站式属性数据失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value()); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 从设备属性映射的"data"值中解析出JSON对象
|
|
|
|
|
|
JSONObject propertiesObject = JSONObject.parseObject(deviceProperties.get("data").toString()); |
|
|
|
|
|
|
|
|
|
|
|
// 检查解析出的JSON对象是否包含"data"键
|
|
|
|
|
|
if (propertiesObject.get("data") == null) { |
|
|
|
|
|
logger.error("获取一站式属性数据失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value()); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 从解析出的JSON对象中获取设备属性数据列表
|
|
|
|
|
|
JSONArray properties = JSONArray.parseArray(propertiesObject.get("data").toString()); |
|
|
|
|
|
|
|
|
|
|
|
// 遍历设备属性数据列表,对每个属性对象进行处理
|
|
|
|
|
|
properties.forEach(propertyObject -> { |
|
|
|
|
|
JSONObject propertyJsonObject = (JSONObject)propertyObject; |
|
|
|
|
|
|
|
|
|
|
|
// 将设备消息转换为交通断面数据统计定义对象
|
|
|
|
|
|
List<DcTrafficSectionData> dcTrafficSectionDataList = convertToTrafficStatistics(propertyJsonObject, DeviceDataCategory.HISTORY); |
|
|
|
|
|
|
|
|
|
|
|
// 如果转换结果非空且不为空列表,则将转换后的数据添加到缓存中
|
|
|
|
|
|
if (dcTrafficSectionDataList != null && !dcTrafficSectionDataList.isEmpty()) { |
|
|
|
|
|
// 将转换后的数据添加到缓存中
|
|
|
|
|
|
dcTrafficSectionDataList.forEach(DailyTrafficStatisticsCache::addCacheData); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 恢复每月交通数据缓存的方法。 |
|
|
* 恢复每月交通数据缓存的方法。 |
|
|
* 通过查询当前月份至今的每日交通数据,并将其添加到每月交通统计缓存中。 |
|
|
* 通过查询当前月份至今的每日交通数据,并将其添加到每月交通统计缓存中。 |
|
@ -87,60 +271,202 @@ public class DcTrafficSectionDataServiceImpl |
|
|
dcTrafficSectionDataList.forEach(YearlyTrafficStatisticsCache::addCacheData); |
|
|
dcTrafficSectionDataList.forEach(YearlyTrafficStatisticsCache::addCacheData); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 处理实时接收到的设备消息,并将其转换为交通断面统计数据对象并缓存。 |
|
|
* 将设备消息转换为交通断面数据统计定义对象 |
|
|
* |
|
|
* |
|
|
* @param msg 设备发送的JSON格式实时消息 |
|
|
* @param msg JSON格式的设备实时消息 |
|
|
|
|
|
* @param category 设备数据类型 |
|
|
|
|
|
* @return 转换后的交通断面数据统计定义对象 |
|
|
*/ |
|
|
*/ |
|
|
@Override |
|
|
|
|
|
public void processRealtimeMessage(JSONObject msg) { |
|
|
|
|
|
|
|
|
|
|
|
// 1. 将设备消息转换为交通断面数据统计定义对象
|
|
|
public List<DcTrafficSectionData> convertToTrafficStatistics(JSONObject msg, DeviceDataCategory category) { |
|
|
DcTrafficSectionData dcTrafficSectionData = convertToTrafficStatistics(msg); |
|
|
// 获取属性
|
|
|
|
|
|
JSONObject property; |
|
|
|
|
|
|
|
|
|
|
|
if (category == DeviceDataCategory.REAL_TIME) { |
|
|
|
|
|
JSONObject properties = msg.getJSONObject("properties"); |
|
|
|
|
|
|
|
|
|
|
|
// 属性数据
|
|
|
|
|
|
if (properties == null) { |
|
|
|
|
|
logger.error("接收实时属性数据,属性数据不存在,请检查物联网一站式设备数据是否正常"); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 获取属性01的数据
|
|
|
|
|
|
property = properties.getJSONObject(IotProductPropertiesEnum.ONE_STOP_PRODUCT_01.getNumber()); |
|
|
|
|
|
} else { |
|
|
|
|
|
property = msg.getJSONObject("value"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 判断是否属性01的数据
|
|
|
|
|
|
if (property == null) { |
|
|
|
|
|
logger.error("非属性01的数据,无法处理,请检查物联网一站式设备数据是否正常"); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 物联设备id
|
|
|
|
|
|
String iotDeviceId = msg.getString("deviceId"); |
|
|
|
|
|
|
|
|
|
|
|
if (iotDeviceId == null || iotDeviceId.isEmpty()) { |
|
|
|
|
|
logger.error("设备id为空,无法处理,请检查物联网平台一站式设备数据是否正常"); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 上报时间(时间戳)
|
|
|
|
|
|
Long timestamp = msg.getLong("timestamp"); |
|
|
|
|
|
|
|
|
|
|
|
if (timestamp == null || timestamp == 0L) { |
|
|
|
|
|
logger.error("上报时间为空,无法处理,请检查物联网平台一站式设备数据是否正常"); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 从缓存中获取设备信息
|
|
|
|
|
|
DcDevice dcDevice = redisCache.getCacheMapValue(RedisKeyConstants.DC_DEVICES, iotDeviceId); |
|
|
|
|
|
|
|
|
|
|
|
if (dcDevice == null) { |
|
|
|
|
|
logger.error("设备信息不存在,请检查是否配置设备信息,物联平台设备id:{}无对应设备信息", iotDeviceId); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 2. 将转换后的数据添加到缓存中
|
|
|
// 上报时间
|
|
|
// DailyTrafficStatisticsCache.addCacheData(dcTrafficSectionData);
|
|
|
Date reportTime = DateUtil.date(timestamp); |
|
|
|
|
|
|
|
|
|
|
|
// 车道信息
|
|
|
|
|
|
JSONArray lanes = property.getJSONArray("lanes"); |
|
|
|
|
|
|
|
|
|
|
|
if (lanes == null || lanes.isEmpty()) { |
|
|
|
|
|
logger.error("车道信息为空,无法处理,请检查物联网平台一站式设备数据是否正常"); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 分别处理上行和下行数据
|
|
|
|
|
|
return new ArrayList<>(processLaneData(lanes, dcDevice, reportTime).values()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 将设备实时消息转换为交通断面数据统计定义对象 |
|
|
* 处理车道数据,计算上行和下行的车流量和累计平均速度。 |
|
|
* |
|
|
* |
|
|
* @param msg JSON格式的设备实时消息 |
|
|
* @param lanes 车道数据的JSON数组,包含各个车道的信息。 |
|
|
* @return 转换后的交通断面数据统计定义对象 |
|
|
* @param dcDevice 设备信息,用于标识数据来源。 |
|
|
|
|
|
* @param reportTime 报告时间,标识数据的时间戳。 |
|
|
|
|
|
* @return 返回一个Map,包含上行和下行的交通段数据。 |
|
|
*/ |
|
|
*/ |
|
|
public DcTrafficSectionData convertToTrafficStatistics(JSONObject msg) { |
|
|
private Map<LaneDirection, DcTrafficSectionData> processLaneData(JSONArray lanes, DcDevice dcDevice, Date reportTime) { |
|
|
|
|
|
|
|
|
|
|
|
Map<LaneDirection, DcTrafficSectionData> resultMap = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
|
|
// 初始化上行和下行的交通段数据
|
|
|
|
|
|
DcTrafficSectionData upwardData = new DcTrafficSectionData(); |
|
|
|
|
|
DcTrafficSectionData downwardData = new DcTrafficSectionData(); |
|
|
|
|
|
|
|
|
|
|
|
initializeTrafficSectionData(upwardData, dcDevice, reportTime, LaneDirection.UPWARD); |
|
|
|
|
|
initializeTrafficSectionData(downwardData, dcDevice, reportTime, LaneDirection.DOWNWARD); |
|
|
|
|
|
|
|
|
|
|
|
// 初始化上行和下行的车流量和累计平均速度
|
|
|
|
|
|
int upwardTrafficVolume = 0, downwardTrafficVolume = 0; |
|
|
|
|
|
double upwardCumulativeAverageSpeed = 0.0, downwardCumulativeAverageSpeed = 0.0; |
|
|
|
|
|
|
|
|
|
|
|
// 遍历车道信息,计算上行和下行的车流量和累计平均速度
|
|
|
|
|
|
for (Object lane : lanes) { |
|
|
|
|
|
// 解析车道数据
|
|
|
|
|
|
JSONObject laneData = (JSONObject) lane; |
|
|
|
|
|
// 获取车道号,并判断是否为下行车道
|
|
|
|
|
|
Integer laneNumber = laneData.getInteger("laneNumber"); |
|
|
|
|
|
boolean isDownward = laneNumber >= 31 || laneNumber == 3; |
|
|
|
|
|
|
|
|
DcTrafficSectionData dcTrafficSectionData = new DcTrafficSectionData(); |
|
|
// 根据车道方向累加车流量和累计平均速度
|
|
|
|
|
|
int totalTrafficFlow = laneData.getInteger("totalTrafficFlow"); |
|
|
|
|
|
double cumulativeAverageSpeed = calculateCumulativeAverageSpeed(laneData); |
|
|
|
|
|
|
|
|
// TODO
|
|
|
if (isDownward) { |
|
|
return dcTrafficSectionData; |
|
|
downwardTrafficVolume += totalTrafficFlow; |
|
|
|
|
|
downwardCumulativeAverageSpeed += cumulativeAverageSpeed; |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
upwardTrafficVolume += totalTrafficFlow; |
|
|
|
|
|
upwardCumulativeAverageSpeed += cumulativeAverageSpeed; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
// 设置上行和下行的车流量和累计平均速度
|
|
|
|
|
|
setUpTrafficSectionData(upwardData, upwardTrafficVolume, upwardCumulativeAverageSpeed); |
|
|
|
|
|
setUpTrafficSectionData(downwardData, downwardTrafficVolume, downwardCumulativeAverageSpeed); |
|
|
|
|
|
|
|
|
|
|
|
// 将上行和下行的交通段数据放入结果映射中
|
|
|
|
|
|
resultMap.put(LaneDirection.UPWARD, upwardData); |
|
|
|
|
|
resultMap.put(LaneDirection.DOWNWARD, downwardData); |
|
|
|
|
|
|
|
|
|
|
|
return resultMap; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 定义每小时第20分钟执行的任务,用于清除过期缓存数据并将缓存中的数据整合后保存至数据库。 |
|
|
* 初始化交通段数据 |
|
|
|
|
|
* @param data 交通段数据对象,用于存储交通数据 |
|
|
|
|
|
* @param dcDevice 设备对象,包含设备的详细信息 |
|
|
|
|
|
* @param reportTime 数据上报时间 |
|
|
|
|
|
* @param direction 车道方向 |
|
|
*/ |
|
|
*/ |
|
|
// @Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务
|
|
|
private void initializeTrafficSectionData(DcTrafficSectionData data, DcDevice dcDevice, Date reportTime, LaneDirection direction) { |
|
|
public void performHourlyCleanupAndPersist() { |
|
|
|
|
|
// 清除已过期的缓存数据
|
|
|
// 设置设备id
|
|
|
DailyTrafficStatisticsCache.clearExpiredData(); |
|
|
data.setDeviceId(dcDevice.getId()); |
|
|
MonthlyTrafficStatisticsCache.clearExpiredData(); |
|
|
// 设置车道方向
|
|
|
QuarterlyTrafficStatisticsCache.clearExpiredData(); |
|
|
data.setDirection(direction.getValue()); |
|
|
YearlyTrafficStatisticsCache.clearExpiredData(); |
|
|
// 设置上报时间
|
|
|
// 整合缓存数据并保存至数据库
|
|
|
data.setReportTime(reportTime); |
|
|
// 将缓存中的数据按日统计后保存至数据库
|
|
|
// 设置数据上报时间
|
|
|
// 添加月交通断面数据到缓存中
|
|
|
data.setStatisticalDate(reportTime); |
|
|
persistAggregatedData(DailyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficStatisticsCache::addCacheData); |
|
|
// 设置统计时段类型为日
|
|
|
// 将缓存中的数据按月统计后保存至数据库
|
|
|
data.setPeriodType(TrafficDataPeriodTypeEnum.DAY); |
|
|
// 添加季度交通断面数据到缓存中
|
|
|
// 将设备的桩号转换为整数后设置
|
|
|
persistAggregatedData(MonthlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficStatisticsCache::addCacheData); |
|
|
data.setStakeMark(dcDevice.stakeMarkToInt()); |
|
|
// 将缓存中的数据按季度统计后保存至数据库
|
|
|
} |
|
|
// 添加年交通断面数据到缓存中
|
|
|
|
|
|
persistAggregatedData(QuarterlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficStatisticsCache::addCacheData); |
|
|
|
|
|
// 将缓存中的数据按年统计后保存至数据库
|
|
|
/** |
|
|
persistAggregatedData(YearlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {}); |
|
|
* 设置交通路段数据。 |
|
|
|
|
|
* 该方法用于根据给定的交通量和累积平均速度,设置交通路段的数据。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param data 交通路段数据对象,用于存储交通路段的相关信息。 |
|
|
|
|
|
* @param trafficVolume 该路段的交通量,单位通常为车辆数。 |
|
|
|
|
|
* @param cumulativeAverageSpeed 该路段的累积平均速度,单位通常为千米/小时。 |
|
|
|
|
|
*/ |
|
|
|
|
|
private void setUpTrafficSectionData(DcTrafficSectionData data, int trafficVolume, double cumulativeAverageSpeed) { |
|
|
|
|
|
data.setTrafficVolume(trafficVolume); // 设置交通量
|
|
|
|
|
|
data.setAverageSpeed(0); |
|
|
|
|
|
if (trafficVolume != 0) { // 当交通量不为0时,计算并设置平均速度
|
|
|
|
|
|
data.setAverageSpeed((int) Math.round(cumulativeAverageSpeed / trafficVolume)); // 平均速度 = 累积平均速度 / 交通量
|
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 计算给定车道数据的累计平均速度。 |
|
|
|
|
|
* 该函数根据laneData中提供的各种车辆类型的数量和平均速度,计算出累计的平均速度。 |
|
|
|
|
|
* |
|
|
|
|
|
* @param laneData 包含车道数据的JSONObject对象,其中包含了各种车辆类型的数量和平均速度等数据。 |
|
|
|
|
|
* @return 返回计算出的累计平均速度。 |
|
|
|
|
|
*/ |
|
|
|
|
|
private int calculateCumulativeAverageSpeed(JSONObject laneData) { |
|
|
|
|
|
// 根据laneData中的数据计算累计平均速度
|
|
|
|
|
|
// 累加平均速度(中小客车)
|
|
|
|
|
|
return laneData.getInteger("trafficNumberOfInAndSmall") * laneData.getInteger("inAndSmallAverageVehicleSpeed") + |
|
|
|
|
|
// 累加平均速度(小型货车)
|
|
|
|
|
|
laneData.getInteger("trafficVolumeOfSmallTrucks") * laneData.getInteger("smallTrucksAverageVehicleSpeed") + |
|
|
|
|
|
// 累加平均速度(大客车)
|
|
|
|
|
|
laneData.getInteger("busTrafficVolume") * laneData.getInteger("averageSpeedOfBus") + |
|
|
|
|
|
// 累加平均速度(中型货车)
|
|
|
|
|
|
laneData.getInteger("mediumTruckTrafficVolume") * laneData.getInteger("averageSpeedOfMediumSizeTrucks") + |
|
|
|
|
|
// 累加平均速度(大型货车)
|
|
|
|
|
|
laneData.getInteger("largeTruckTrafficVolume") * laneData.getInteger("averageSpeedOfLargeTrucks") + |
|
|
|
|
|
// 累加平均速度(特大型货车)
|
|
|
|
|
|
laneData.getInteger("extraLargeTrucksTrafficVolume") * laneData.getInteger("averageSpeedOfExtraLargeTrucks") + |
|
|
|
|
|
// 累加平均速度(集装箱车)
|
|
|
|
|
|
laneData.getInteger("containerTruckTrafficVolume") * laneData.getInteger("averageSpeedOfContainerTruck") + |
|
|
|
|
|
// 累加平均速度(拖拉机)
|
|
|
|
|
|
laneData.getInteger("tractorTrafficVolume") * laneData.getInteger("averageSpeedOfTractor") + |
|
|
|
|
|
// 累加平均速度(摩托车)
|
|
|
|
|
|
laneData.getInteger("motorcycleTrafficVolume") * laneData.getInteger("averageSpeedOfMotorcycle"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|