|
|
|
@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
|
|
|
|
|
import com.ruoyi.business.domain.HwDeviceModeFunction;
|
|
|
|
|
import com.ruoyi.business.domain.VO.DeviceDataColumnVo;
|
|
|
|
|
import com.ruoyi.business.domain.VO.DeviceHistoryDataVo;
|
|
|
|
|
import com.ruoyi.business.domain.VO.DeviceHistoryGroupDataVo;
|
|
|
|
|
import com.ruoyi.business.domain.VO.DeviceLatestDataVo;
|
|
|
|
|
import com.ruoyi.business.mapper.HwDeviceModeFunctionMapper;
|
|
|
|
|
import com.ruoyi.business.service.IHwMonitorPlatformService;
|
|
|
|
@ -11,20 +12,18 @@ import com.ruoyi.common.core.constant.HwDictConstants;
|
|
|
|
|
import com.ruoyi.common.core.constant.SecurityConstants;
|
|
|
|
|
import com.ruoyi.common.core.constant.TdEngineConstants;
|
|
|
|
|
import com.ruoyi.tdengine.api.RemoteTdEngineService;
|
|
|
|
|
import com.ruoyi.tdengine.api.domain.TdField;
|
|
|
|
|
import com.ruoyi.tdengine.api.domain.TdHistorySelectDto;
|
|
|
|
|
import com.ruoyi.tdengine.api.domain.TdReturnDataVo;
|
|
|
|
|
import com.ruoyi.tdengine.api.domain.TdSuperTableSelectVo;
|
|
|
|
|
import com.ruoyi.tdengine.api.domain.*;
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
|
import java.time.temporal.ChronoUnit;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
import java.time.*;
|
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @Description: 监控平台数据处理接口
|
|
|
|
@ -205,4 +204,284 @@ public class HwMonitorPlatformServiceImpl implements IHwMonitorPlatformService {
|
|
|
|
|
return deviceDataColumnVos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return TdReturnDataVo
|
|
|
|
|
* @param: deviceHistoryGroupDataVo
|
|
|
|
|
* @description 按日期分组获取设备历史数据
|
|
|
|
|
* @author xins
|
|
|
|
|
* @date 2024-12-31 16:23
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public TdReturnDataVo getGroupDeviceData(DeviceHistoryGroupDataVo deviceHistoryGroupDataVo) {
|
|
|
|
|
TdGroupSelectDto tdGroupSelectDto = new TdGroupSelectDto();
|
|
|
|
|
tdGroupSelectDto.setDatabaseName(TdEngineConstants.getDatabaseName());
|
|
|
|
|
tdGroupSelectDto.setTableName(TdEngineConstants.getDeviceDataTableName(deviceHistoryGroupDataVo.getDeviceId()));
|
|
|
|
|
tdGroupSelectDto.setFirstFieldName(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME);
|
|
|
|
|
|
|
|
|
|
String functionIdentifier = deviceHistoryGroupDataVo.getFunctionIdentifier().equalsIgnoreCase("value") ?
|
|
|
|
|
"value1" : deviceHistoryGroupDataVo.getFunctionIdentifier();
|
|
|
|
|
tdGroupSelectDto.setStartTime(deviceHistoryGroupDataVo.getStartTime());
|
|
|
|
|
tdGroupSelectDto.setEndTime(deviceHistoryGroupDataVo.getEndTime());
|
|
|
|
|
tdGroupSelectDto.setFunctionIdentifier(functionIdentifier);
|
|
|
|
|
tdGroupSelectDto.setInterval(HwDictConstants.intervalMap.get(deviceHistoryGroupDataVo.getInterval()));
|
|
|
|
|
tdGroupSelectDto.setOffset(deviceHistoryGroupDataVo.getOffset() * deviceHistoryGroupDataVo.getLimit());
|
|
|
|
|
tdGroupSelectDto.setLimit(deviceHistoryGroupDataVo.getLimit());
|
|
|
|
|
|
|
|
|
|
return this.remoteTdEngineService.getGroupDeviceData(tdGroupSelectDto, SecurityConstants.INNER).getData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// private static Map<String, Object> createEvent(String timestamp, double value) {
|
|
|
|
|
// Map<String, Object> event = new HashMap<>();
|
|
|
|
|
// event.put("timestamp", Instant.parse(timestamp));
|
|
|
|
|
// event.put("value", value);
|
|
|
|
|
// return event;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// public List<Map<LocalDate, Double>> groupDeviceData(List<Map<String, Object>> historyData, DeviceHistoryDataVo historyDataVo) {
|
|
|
|
|
// // 创建一个包含多个 Map 对象的列表
|
|
|
|
|
// List<String> functionIdentifiers = historyDataVo.getFunctionIdentifiers();
|
|
|
|
|
// String functionIdentifer = functionIdentifiers.get(0);
|
|
|
|
|
//
|
|
|
|
|
// // 设置时间戳的格式
|
|
|
|
|
// DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
|
// DateTimeFormatter hourFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
|
|
|
|
|
//
|
|
|
|
|
// Map<LocalDateTime, Double> deviceDataGroup = new HashMap<>();
|
|
|
|
|
// // 按INTERVAL分组
|
|
|
|
|
// String interval = historyDataVo.getInterval();
|
|
|
|
|
// String intervalType = historyDataVo.getIntervalType();
|
|
|
|
|
// switch (interval) {
|
|
|
|
|
// case HwDictConstants.INTERVAL_HOUR:
|
|
|
|
|
// switch (intervalType) {
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_AVG:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME).truncatedTo(ChronoUnit.HOURS),
|
|
|
|
|
// Collectors.averagingDouble(e -> (double) e.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_SUM:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get("ts"), DateTimeFormatter.ISO_OFFSET_DATE_TIME).truncatedTo(ChronoUnit.HOURS),
|
|
|
|
|
// Collectors.summingDouble(event -> (double) event.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MAX:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME).truncatedTo(ChronoUnit.HOURS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.maxBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MIN:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME).truncatedTo(ChronoUnit.HOURS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.minBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_DAY:
|
|
|
|
|
// switch (intervalType) {
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_AVG:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.DAYS),
|
|
|
|
|
// Collectors.averagingDouble(e -> (double) e.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_SUM:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get("ts"), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.DAYS),
|
|
|
|
|
// Collectors.summingDouble(event -> (double) event.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MAX:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.DAYS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.maxBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MIN:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.DAYS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.minBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_WEEK:
|
|
|
|
|
// switch (intervalType) {
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_AVG:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.WEEKS),
|
|
|
|
|
// Collectors.averagingDouble(e -> (double) e.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_SUM:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get("ts"), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.WEEKS),
|
|
|
|
|
// Collectors.summingDouble(event -> (double) event.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MAX:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.WEEKS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.maxBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MIN:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.WEEKS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.minBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_MONTH:
|
|
|
|
|
// switch (intervalType) {
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_AVG:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.MONTHS),
|
|
|
|
|
// Collectors.averagingDouble(e -> (double) e.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_SUM:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get("ts"), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.MONTHS),
|
|
|
|
|
// Collectors.summingDouble(event -> (double) event.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MAX:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.MONTHS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.maxBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MIN:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.MONTHS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.minBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_YEAR:
|
|
|
|
|
// switch (intervalType) {
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_AVG:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.YEARS),
|
|
|
|
|
// Collectors.averagingDouble(e -> (double) e.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_SUM:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get("ts"), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.YEARS),
|
|
|
|
|
// Collectors.summingDouble(event -> (double) event.get(functionIdentifer))
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MAX:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.YEARS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.maxBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// case HwDictConstants.INTERVAL_TYPE_MIN:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.YEARS),
|
|
|
|
|
// Collectors.collectingAndThen(
|
|
|
|
|
// Collectors.minBy(Comparator.comparingDouble(e -> (double) e.get(functionIdentifer))),
|
|
|
|
|
// opt -> opt.map(e -> (double) e.get(functionIdentifer)).orElse(Double.NaN)
|
|
|
|
|
// )
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
// break;
|
|
|
|
|
// default:
|
|
|
|
|
// deviceDataGroup = historyData.stream()
|
|
|
|
|
// .collect(Collectors.groupingBy(
|
|
|
|
|
// event -> LocalDateTime.parse((String) event.get(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
|
|
|
|
// .truncatedTo(ChronoUnit.SECONDS),
|
|
|
|
|
// e -> (double) e.get(functionIdentifer)
|
|
|
|
|
// ));
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// // 分组的结果
|
|
|
|
|
// System.out.println("Max Values by:"+interval);
|
|
|
|
|
// deviceDataGroup.forEach((day, maxValue) -> {
|
|
|
|
|
// String formattedDay = dayFormatter.format(day);
|
|
|
|
|
// System.out.println("Day: " + formattedDay + ", Max Value: " + maxValue);
|
|
|
|
|
// });
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// return deviceDataGroup;
|
|
|
|
|
//
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|