This commit is contained in:
刘玉霞 2024-11-13 10:46:46 +08:00
commit 156b3c7ee7
7 changed files with 199 additions and 94 deletions

View File

@ -35,8 +35,8 @@ public class FaultRecorderController {
} }
@RequestMapping(value = "/parseData", method = RequestMethod.POST) @RequestMapping(value = "/parseData", method = RequestMethod.POST)
public R<Map<String, List<String>>> parseData(@RequestBody JSONObject jsonObject) throws IOException { public R<Map<String, List<Object>>> parseData(@RequestBody JSONObject jsonObject) throws IOException {
Map<String, List<String>> dataCurve = faultRecorderService.getDataCurve(jsonObject.getString("url"), jsonObject.getString("deviceCode")); Map<String, List<Object>> dataCurve = faultRecorderService.getDataCurve(jsonObject.getString("url"), jsonObject.getString("deviceCode"));
return R.success(dataCurve); return R.success(dataCurve);
} }

View File

@ -13,7 +13,7 @@ import java.util.Map;
public interface FaultRecorderService { public interface FaultRecorderService {
List<FileNode> getDirOrFileList(String name,String startTime, String endTime); List<FileNode> getDirOrFileList(String name,String startTime, String endTime);
Map<String, List<String>> getDataCurve(String url, String deviceCode) throws IOException; Map<String, List<Object>> getDataCurve(String url, String deviceCode) throws IOException;
void updateFdrConfig(SysEquipment sysEquipment); void updateFdrConfig(SysEquipment sysEquipment);

View File

@ -24,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.*; import java.io.*;
import java.nio.file.Path; import java.nio.file.Path;
import java.rmi.ServerException;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDate; import java.time.LocalDate;
@ -96,11 +97,9 @@ public class FaultRecorderServiceImpl implements FaultRecorderService {
} }
@Override @Override
public Map<String, List<String>> getDataCurve(String url, String deviceCode) throws IOException { public Map<String, List<Object>> getDataCurve(String url, String deviceCode) throws IOException {
Map<String, List<String>> resultMap = null; Map<String, List<Object>> resultMap = null;
InputStream fileStream = null; try (InputStream fileStream = minioViewsServcie.getFileStream(url)) {
try {
fileStream = minioViewsServcie.getFileStream(url);
//根据device Code查询故障录波格式 //根据device Code查询故障录波格式
QueryWrapper<SysEquipment> queryWrapper = new QueryWrapper<>(); QueryWrapper<SysEquipment> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("CODE", deviceCode); queryWrapper.eq("CODE", deviceCode);
@ -109,19 +108,17 @@ public class FaultRecorderServiceImpl implements FaultRecorderService {
throw new ServiceException("设备不存在,请选择正确设备"); throw new ServiceException("设备不存在,请选择正确设备");
} }
if (StringUtils.isBlank(sysEquipment.getFdrFormat())){
throw new ServerException("请添加设备故障录波配置");
}
FdrFormatVo fdrFormatVo = JSON.parseObject(sysEquipment.getFdrFormat(), FdrFormatVo.class); FdrFormatVo fdrFormatVo = JSON.parseObject(sysEquipment.getFdrFormat(), FdrFormatVo.class);
// 解析文件内容 // 解析文件内容
List<List<String>> parsedData = parseFile(fileStream, fdrFormatVo.getDelimiter(), fdrFormatVo.getValidStartLine()); resultMap = parseFile(fileStream, fdrFormatVo.getTimeFormat(), fdrFormatVo.getDelimiter(), fdrFormatVo.getValidStartLine());
resultMap = parseDataCurve(parsedData, fdrFormatVo.getTimeFormat());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
}finally {
if (fileStream != null){
fileStream.close();
}
} }
return resultMap; return resultMap;
} }
@ -131,9 +128,11 @@ public class FaultRecorderServiceImpl implements FaultRecorderService {
sysEquipmentMapper.updateById(sysEquipment); sysEquipmentMapper.updateById(sysEquipment);
} }
public List<List<String>> parseFile(InputStream inputStream, String delimiter, int validStartLine) { public Map<String, List<Object>> parseFile(InputStream inputStream, String timeFormat, String delimiter, int validStartLine) {
List<List<String>> result = new ArrayList<>(); List<List<String>> result = new ArrayList<>();
Map<String, List<Object>> stringListMap = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line; String line;
int lineNumber = 0; int lineNumber = 0;
@ -148,36 +147,44 @@ public class FaultRecorderServiceImpl implements FaultRecorderService {
List<String> lineData = Arrays.stream(line.split(delimiter)).toList(); List<String> lineData = Arrays.stream(line.split(delimiter)).toList();
result.add(lineData); result.add(lineData);
} }
stringListMap = parseDataCurve(result, timeFormat);
} catch (Exception e) { } catch (Exception e) {
log.error("文件解析失败{}", e);
log.error("文件解析失败{}",e);
} }
return result; return stringListMap;
} }
public Map<String, List<String>> parseDataCurve(List<List<String>> data, String timeFormat) throws ParseException { public Map<String, List<Object>> parseDataCurve(List<List<String>> data, String timeFormat) throws ParseException {
List<String> listField = data.get(0); List<String> listField = data.get(0);
Map<String, List<String>> map = new HashMap<>(); Map<String, List<Object>> map = new HashMap<>();
data.remove(0); data.remove(0);
for (List<String> item : data) { for (List<String> item : data) {
for (int i = 0; i < item.size(); i++) { for (int i = 0; i < item.size(); i++) {
if (map.get(listField.get(i)) == null) { if (map.get(listField.get(i)) == null) {
List<String> valueList = new ArrayList<>(); if (i == 0){
valueList.add(item.get(i)); List<Object> timeList = new ArrayList<>();
map.put(listField.get(i), valueList); long timestamp = convertToTimestamp(item.get(i), timeFormat);
timeList.add(timestamp);
map.put(listField.get(i),timeList);
}else {
List<Object> valueList = new ArrayList<>();
valueList.add(Double.valueOf(item.get(i)));
map.put(listField.get(i), valueList);
}
} else { } else {
List<String> valueList = map.get(listField.get(i)); List<Object> valueList = map.get(listField.get(i));
valueList.add(item.get(i)); if (i == 0){
valueList.add(convertToTimestamp(item.get(i),timeFormat));
}
else {
valueList.add(Double.valueOf(item.get(i)));
}
} }
} }
} }
List<String> list = map.get(listField.get(0));
List<String> timeList = new ArrayList<>();
for (String item : list) {
long timestamp = convertToTimestamp(item, timeFormat);
timeList.add(String.valueOf(timestamp));
map.put(listField.get(0), timeList);
}
return map; return map;
} }

View File

@ -36,49 +36,85 @@ export const runAirBlowerReq = (
}) })
} }
export const getReportTemplateListReq = (data: { category: '单机报表' | '多机报表', pageNum: number, pageSize: number }) => { export const getReportTemplateListReq = (data: { category: '单机报表' | '多机报表'; pageNum: number; pageSize: number }) => {
return createAxios<never, Promise<{ return createAxios<
code: number, never,
msg: string, Promise<{
data: { code: number
total: number,
rows: { id: string, category: '单机报表' | '多机报表', template: string }[]
code: number,
msg: string msg: string
}, data: {
success: boolean total: number
}>>({ rows: { id: string; category: '单机报表' | '多机报表'; template: string }[]
code: number
msg: string
}
success: boolean
}>
>({
url: '/api/page/report/template/getList', url: '/api/page/report/template/getList',
method: 'post', method: 'post',
data data,
}) })
} }
export const addReportTemplateListReq = (data: { category: '单机报表' | '多机报表', template: string }) => { export const addReportTemplateListReq = (data: { category: '单机报表' | '多机报表'; template: string }) => {
return createAxios<never, Promise<{ return createAxios<
code: number, never,
msg: string, Promise<{
data: { code: number
id: string, msg: string
category: '单机报表' | '多机报表', data: {
template: string id: string
}[], category: '单机报表' | '多机报表'
success: boolean template: string
}>>({ }[]
success: boolean
}>
>({
url: '/api/page/report/template/add', url: '/api/page/report/template/add',
method: 'post', method: 'post',
data data,
}) })
} }
export const delReportTemplateListReq = (data: { id: string }) => { export const delReportTemplateListReq = (data: { id: string }) => {
return createAxios<never, Promise<{ return createAxios<
code: number, never,
msg: string, Promise<{
success: boolean code: number
}>>({ msg: string
success: boolean
}>
>({
url: '/api/page/report/template/del', url: '/api/page/report/template/del',
method: 'post', method: 'post',
data data,
})
}
export function powerCurveExport(params: object = {}) {
return createAxios({
url: '/api/page/statistical/powerCurveExport',
method: 'POST',
data: params,
responseType: 'blob',
})
}
export function trendContrastExport(params: object = {}) {
return createAxios({
url: '/api/page/statistical/trendContrastExport',
method: 'POST',
data: params,
responseType: 'blob',
})
}
export function trendAnalyseExport(params: object = {}) {
return createAxios({
url: '/api/page/statistical/trendAnalyseExport',
method: 'POST',
data: params,
responseType: 'blob',
}) })
} }

View File

@ -11,7 +11,6 @@
</div> </div>
<div class="topRight"> <div class="topRight">
<el-button type="primary" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button> <el-button type="primary" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button>
<el-button style="color: #0064aa" @click="statAnalysiImport()">{{ t('statAnalysis.import') }}</el-button>
<el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button> <el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button>
</div> </div>
</el-header> </el-header>
@ -61,7 +60,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onMounted } from 'vue' import { reactive, ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { queryWindTurbinesPages, historyReq } from '/@/api/backend/statAnalysis/request' import { queryWindTurbinesPages, historyReq, powerCurveExport } from '/@/api/backend/statAnalysis/request'
import { theoreticalpowerCurveList, powerCurveQuery } from '/@/api/backend/theoreticalpowerCurve/request' import { theoreticalpowerCurveList, powerCurveQuery } from '/@/api/backend/theoreticalpowerCurve/request'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import * as echarts from 'echarts' import * as echarts from 'echarts'
@ -325,8 +324,33 @@ const historyDataReq = (data: any) => {
}) })
} }
const statAnalysisExport = () => {} const statAnalysisExport = () => {
const statAnalysiImport = () => {} const params = statAnalysisFatory.value ? statAnalysisFatory.value : statAnalysisDeviceId.value
const requestData = {
devices: [
{
deviceId: statAnalysisDeviceId.value.split(':')[2],
attributes: ['iGenPower', 'iWindSpeed'],
},
],
interval: statAnalysisInterval.value || '5m',
startTime: new Date(statAnalysisTime.value[0]).getTime(),
endTime: new Date(statAnalysisTime.value[1]).getTime(),
madeinfactory: params.split(':')[0],
model: params.split(':')[1],
}
console.log(requestData)
powerCurveExport(requestData).then((res: any) => {
const downloadUrl = window.URL.createObjectURL(res)
const a = document.createElement('a')
a.href = downloadUrl
a.download = '功率曲线' + new Date().getTime()
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(downloadUrl)
document.body.removeChild(a)
})
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.statAnalysis { .statAnalysis {

View File

@ -31,7 +31,6 @@
</div> </div>
<div class="topRight"> <div class="topRight">
<el-button type="primary" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button> <el-button type="primary" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button>
<el-button style="color: #0064aa" @click="statAnalysiImport()">{{ t('statAnalysis.import') }}</el-button>
<el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button> <el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button>
</div> </div>
</el-header> </el-header>
@ -101,7 +100,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onMounted, markRaw } from 'vue' import { reactive, ref, onMounted, markRaw } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { queryWindTurbinesPages, historyReq } from '/@/api/backend/statAnalysis/request' import { queryWindTurbinesPages, historyReq, trendAnalyseExport } from '/@/api/backend/statAnalysis/request'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { DArrowRight, Plus, Delete } from '@element-plus/icons-vue' import { DArrowRight, Plus, Delete } from '@element-plus/icons-vue'
import MeasurementPage from './analysisAttributes.vue' import MeasurementPage from './analysisAttributes.vue'
@ -419,8 +418,37 @@ const historyDataReq = (data: any, index: number) => {
}) })
} }
const statAnalysisExport = () => {} const statAnalysisExport = () => {
const statAnalysiImport = () => {} const requestData: any = []
times.forEach((time: any, index: number) => {
if (time[0] && time[1]) {
const devices = {
devices: [
{
deviceId: statAnalysisSelect.deviceId,
attributes: [statAnalysisSelect.attributeCode],
},
],
interval: statAnalysisSelect.interval || '5m',
startTime: new Date(time[0]).getTime(),
endTime: new Date(time[1]).getTime(),
timeName: customName[index],
}
requestData.push(devices)
}
})
console.log(requestData)
trendAnalyseExport(requestData).then((res: any) => {
const downloadUrl = window.URL.createObjectURL(res)
const a = document.createElement('a')
a.href = downloadUrl
a.download = '趋势分析' + new Date().getTime()
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(downloadUrl)
document.body.removeChild(a)
})
}
function calculateStats(numbers: any) { function calculateStats(numbers: any) {
const max = Math.max(...numbers) const max = Math.max(...numbers)

View File

@ -23,7 +23,6 @@
</div> </div>
<div class="topRight"> <div class="topRight">
<el-button type="primary" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button> <el-button type="primary" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button>
<el-button style="color: #0064aa" @click="statAnalysiImport()">{{ t('statAnalysis.import') }}</el-button>
<el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button> <el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button>
</div> </div>
</el-header> </el-header>
@ -107,7 +106,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { markRaw, reactive, ref, watch, nextTick, onMounted, computed } from 'vue' import { markRaw, reactive, ref, watch, nextTick, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { queryWindTurbinesPages, historyReq } from '/@/api/backend/statAnalysis/request' import { queryWindTurbinesPages, historyReq, trendContrastExport } from '/@/api/backend/statAnalysis/request'
import { ElMessage, ElMenu } from 'element-plus' import { ElMessage, ElMenu } from 'element-plus'
import { DArrowRight, Plus, Delete } from '@element-plus/icons-vue' import { DArrowRight, Plus, Delete } from '@element-plus/icons-vue'
import MeasurementPage from './analysisAttributes.vue' import MeasurementPage from './analysisAttributes.vue'
@ -325,26 +324,7 @@ const getDateRange = (type: 'week' | 'month') => {
} }
const statAnalysisOperate = () => { const statAnalysisOperate = () => {
option.series = [] historyDataReq(getRequestData())
chart.value.setOption(option, { notMerge: true })
const attributes = statAnalysisAttributeCode
const devices = statAnalysisDeviceId.reduce((deviceId: any, curr, index) => {
const existing: any = deviceId.find((item: any) => item.deviceId === curr)
if (existing) {
existing.attributes.push(statAnalysisAttributeCode[index])
} else {
deviceId.push({ deviceId: curr, attributes: [statAnalysisAttributeCode[index]] })
}
return deviceId
}, [])
const requestData = {
devices: devices,
interval: statAnalysisInterval.value || '5m',
startTime: new Date(statAnalysisTime.value[0]).getTime(),
endTime: new Date(statAnalysisTime.value[1]).getTime(),
}
historyDataReq(requestData)
} }
const calculate: any = reactive([{ max: '', min: '', average: '' }]) const calculate: any = reactive([{ max: '', min: '', average: '' }])
const historyDataReq = (data: any) => { const historyDataReq = (data: any) => {
@ -399,8 +379,38 @@ const findAllOccurrences = (arr: any, target: any) => {
const getCommonElements = (arr1: any, arr2: any) => { const getCommonElements = (arr1: any, arr2: any) => {
return arr1.filter((item: any) => arr2.some((x: any) => x === item)) return arr1.filter((item: any) => arr2.some((x: any) => x === item))
} }
const statAnalysisExport = () => {} const statAnalysisExport = () => {
const statAnalysiImport = () => {} console.log('🚀 ~ trendContrastExport ~ getRequestData():', getRequestData())
trendContrastExport(getRequestData()).then((res: any) => {
const downloadUrl = window.URL.createObjectURL(res)
const a = document.createElement('a')
a.href = downloadUrl
a.download = '趋势对比' + new Date().getTime()
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(downloadUrl)
document.body.removeChild(a)
})
}
const getRequestData = () => {
const devices = statAnalysisDeviceId.reduce((deviceId: any, curr, index) => {
const existing: any = deviceId.find((item: any) => item.deviceId === curr)
if (existing) {
existing.attributes.push(statAnalysisAttributeCode[index])
} else {
deviceId.push({ deviceId: curr, attributes: [statAnalysisAttributeCode[index]] })
}
return deviceId
}, [])
const requestData = {
devices: devices,
interval: statAnalysisInterval.value || '5m',
startTime: new Date(statAnalysisTime.value[0]).getTime(),
endTime: new Date(statAnalysisTime.value[1]).getTime(),
}
return requestData
}
function calculateStats(numbers: any) { function calculateStats(numbers: any) {
const max = Math.max(...numbers) const max = Math.max(...numbers)
const min = Math.min(...numbers) const min = Math.min(...numbers)