Chapter IV statement analysis
Product prototype address:
https://app.mockplus.cn/run/prototype/wDiGCuki3_/iLu4KtCQ8ew/1Bv0BoAUyv
1. Equipment status statistics
1.1 demand analysis
In the system, the percentage distribution of equipment in different states shall be displayed in the form of pie chart on the home page. The specific effect is shown in the figure below:
![](images/4-1.png)
1.2 implementation ideas
(1) Write a method to obtain the total number of disconnected devices, disconnected devices and alarms from ElasticSearch
(2) By calculating the number of normal devices, the data is combined into the data format required by the front end and returned
1.3 code implementation
1.3.1 equipment quantity statistics
(1) Add a method to count the total number of devices in ESRepository:
/** * Count the number of all equipment * @return */ public Long getAllDeviceCount(){ CountRequest countRequest=new CountRequest("devices"); countRequest.query( QueryBuilders.matchAllQuery() ); try { CountResponse response = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT); return response.getCount(); } catch (IOException e) { e.printStackTrace(); return 0L; } }
(2) Add a method to count the number of offline devices in ESRepository:
/** * Count the number of all offline devices * @return */ public Long getOfflineCount(){ CountRequest countRequest=new CountRequest("devices"); BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery(); boolQueryBuilder.must( QueryBuilders.termQuery("online",false) ); countRequest.query( boolQueryBuilder ); try { CountResponse response = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT); return response.getCount(); } catch (IOException e) { e.printStackTrace(); return 0L; } }
(3) Add a method to count the number of alarm devices in ESRepository:
/** * Count the number of all alarm devices * @return */ public Long getAlarmCount(){ CountRequest countRequest=new CountRequest("devices"); BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery(); boolQueryBuilder.must( QueryBuilders.termQuery("online",true) ); boolQueryBuilder.must( QueryBuilders.termQuery("alarm",true) ); countRequest.query( boolQueryBuilder ); try { CountResponse response = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT); return response.getCount(); } catch (IOException e) { e.printStackTrace(); return 0L; } }
Write unit tests:
@Test public void testCount(){ Long allDeviceCount = esRepository.getAllDeviceCount();//Total equipment System.out.println("Total equipment:"+allDeviceCount); Long offlineCount = esRepository.getOfflineCount();//Number of offline devices System.out.println("Offline device:"+offlineCount); Long alarmCount = esRepository.getAlarmCount();//Number of alarm equipment System.out.println("Alarm equipment:"+alarmCount); }
1.3.2 equipment status distribution
(1) Add vo for encapsulation of status proportion
The chart tool used in the front end is EChars. The data format required for EChars pie chart is as follows:
data: [ {value: 10200, name: 'Normal operation'}, {value: 310, name: 'off-line'}, {value: 234, name: 'call the police'} ]
API is defined as follows:
![](images/4-90.png)
We define vo according to the format required by the front end
package com.yikekong.vo; import lombok.Data; @Data public class PieVO { private String name;//name private Long value;//value }
(2) Create the report service interface ReportService, and define the method to obtain the device status distribution
package com.yikekong.service; import com.yikekong.vo.PieVO; import java.util.List; /** * Report service */ public interface ReportService { /** * Equipment status distribution * @return */ List<PieVO> getStatusCollect(); }
Create an implementation class and implement the method in the implementation class
package com.yikekong.service.impl; import com.google.common.collect.Lists; import com.yikekong.es.ESRepository; import com.yikekong.service.ReportService; import com.yikekong.vo.PieVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service @Slf4j public class ReportServiceImpl implements ReportService { @Autowired private ESRepository esRepository; @Override public List<PieVO> getStatusCollect() { Long allDeviceCount = esRepository.getAllDeviceCount();//Total equipment quantity Long offlineCount = esRepository.getOfflineCount();//Number of offline devices Long alarmCount = esRepository.getAlarmCount();//Number of alarm devices PieVO devicePie=new PieVO(); devicePie.setName("Normal operation"); devicePie.setValue(allDeviceCount-offlineCount-alarmCount); PieVO offlinePie=new PieVO(); offlinePie.setName("off-line"); offlinePie.setValue(offlineCount); PieVO alarmPie =new PieVO(); alarmPie.setName("call the police"); alarmPie.setValue(alarmCount); List<PieVO> pieVOList= Lists.newArrayList(); pieVOList.add(devicePie); pieVOList.add(offlinePie); pieVOList.add(alarmPie); return pieVOList; } }
(3) New method of DeviceController
package com.yikekong.controller; import com.yikekong.service.ReportService; import com.yikekong.vo.PieVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("/report") public class ReportController { @Autowired private ReportService reportService; /** * Equipment status distribution * @return */ @GetMapping("/statusCollect") public List<PieVO> getStatusCollect(){ return reportService.getStatusCollect(); } }
1.3.3 real time monitoring data
The home page also needs to display the number of devices and the number of alarm devices
![](images/4-4.png)
API design:
![](images/4-5.png)
(1) Create vo class MonitorVO
/** * Real time monitoring data */ @Data public class MonitorVO implements Serializable{ /** * Number of equipment */ private Long deviceCount; /** * Number of alarm devices */ private Long alarmCount; }
(2) New method of ReportController
@Autowired private ESRepository esRepository; /** * Obtain real-time monitoring data * @return */ @GetMapping("/monitor") public MonitorVO getMonitorData(){ MonitorVO monitor = new MonitorVO(); monitor.setDeviceCount(esRepository.getAllDeviceCount()); monitor.setAlarmCount(esRepository.getAlarmCount()); return monitor; }
2. Abnormal alarm Trend Report
2.1 demand analysis
The system shall be able to summarize the total number of exceptions reported at each time point according to the current 1 hour (the first data per minute, the current minute has no data, and the last data is displayed), the current 1 day (the first data per hour, the current hour has no data, and the last data is displayed), and the current 7 days (the display of 7-day hour data), Then, the front-end displays the data in the form of discount chart.
The specific time filtering and presentation format is as follows:
![](images/3-7.png)
The horizontal axis shows the time, and the vertical axis shows the total number of alarm times.
2.2 realization ideas
(1) Query the quota table in influxdb and make grouping statistics according to time points
(2) In the service layer, the statement is encapsulated and transformed into the data format required by the front end
2.3 code implementation
2.3.1 incluxdb time grouping statistics
The date format command can display the year, month, day, hour, minute and second after execution
precision rfc3339
Execute query
select count(value) from quota where alarm='1' and time>='2020-09-01' and time<='2020-09-30' group by time(1d)
Get running results
![](images/3-9.png)
It should be noted here:
The time function of InfluxDB is used in the group by group in the sql to be executed
- group by time(1m): Group summary by minute
- group by time(1h): group and summarize by hour
- Group by time (1D): Group summary by day
Then, with the count function in select, we can get the total number in the summary in the specific time dimension, so that we can get the summary data we want.
In addition to returning pointValue according to the sql statement, InfluxDB will also return the time column corresponding to each data.
2.3.2 alarm trend statistics
(1) Define the pojo class of query results from incluxdb:
package com.yikekong.dto; import lombok.Data; import org.influxdb.annotation.Column; import org.influxdb.annotation.Measurement; import java.io.Serializable; /** * Trend index point */ @Data @Measurement(name = "quota") public class TrendPoint implements Serializable{ /** * time */ @Column(name = "time") private String time; /** * Point in time data */ @Column(name = "pointValue") private Integer pointValue; }
(3) Define the method to obtain the abnormal indicator trend in the ReportService interface:
/** * Obtain abnormal trend indicators * @param start Start time yyyy MM DD HH: mm: SS * @param end End time yyyy MM DD HH: mm: SS * @param type Time statistics type (within 1:60 minutes, 2:24 hours on the same day, 3:7 days) * @return */ List<TrendPoint> getAlarmTrend(String start, String end, int type);
Implement this method in the implementation class ReportServiceImpl:
@Autowired private InfluxRepository influxRepository; @Override public List<TrendPoint> getAlarmTrend(String start, String end, int type) { StringBuilder ql=new StringBuilder("select count(value) as pointValue from quota where alarm='1' "); ql.append("and time>='"+ start +"' and time<='"+ end+"' "); if(type==1){ ql.append("group by time(1m)"); } if(type==2){ ql.append("group by time(1h)"); } if(type==3){ ql.append("group by time(1d)"); } List<TrendPoint> trendPointList = influxRepository.query(ql.toString(), TrendPoint.class); return trendPointList; }
Unit test:
@Autowired private ReportService reportService; @Test public void testAlarmTrend(){ List<TrendPoint> trendPointList = reportService.getAlarmTrend("2020-09-01", "2020-09-30", 3); for(TrendPoint trendPoint:trendPointList){ try { System.out.println(JsonUtil.serialize(trendPoint)); } catch (JsonProcessingException e) { e.printStackTrace(); } } }
The second parameter of getAlarmTrend is 1, 2 and 3 respectively. Observe the test results. The test results with parameter 3 are as follows:
{"time":"2020-09-01T00:00:00Z","pointValue":0} {"time":"2020-09-02T00:00:00Z","pointValue":0} {"time":"2020-09-03T00:00:00Z","pointValue":0} {"time":"2020-09-04T00:00:00Z","pointValue":0} {"time":"2020-09-05T00:00:00Z","pointValue":0} {"time":"2020-09-06T00:00:00Z","pointValue":0} {"time":"2020-09-07T00:00:00Z","pointValue":0} {"time":"2020-09-08T00:00:00Z","pointValue":0} {"time":"2020-09-09T00:00:00Z","pointValue":0} {"time":"2020-09-10T00:00:00Z","pointValue":0} {"time":"2020-09-11T00:00:00Z","pointValue":0} {"time":"2020-09-12T00:00:00Z","pointValue":0} {"time":"2020-09-13T00:00:00Z","pointValue":0} {"time":"2020-09-14T00:00:00Z","pointValue":0} {"time":"2020-09-15T00:00:00Z","pointValue":0} {"time":"2020-09-16T00:00:00Z","pointValue":0} {"time":"2020-09-17T00:00:00Z","pointValue":0} {"time":"2020-09-18T00:00:00Z","pointValue":0} {"time":"2020-09-19T00:00:00Z","pointValue":2} {"time":"2020-09-20T00:00:00Z","pointValue":0} {"time":"2020-09-21T00:00:00Z","pointValue":0} {"time":"2020-09-22T00:00:00Z","pointValue":2379} {"time":"2020-09-23T00:00:00Z","pointValue":0} {"time":"2020-09-24T00:00:00Z","pointValue":14412} {"time":"2020-09-25T00:00:00Z","pointValue":5} {"time":"2020-09-26T00:00:00Z","pointValue":0} {"time":"2020-09-27T00:00:00Z","pointValue":0} {"time":"2020-09-28T00:00:00Z","pointValue":0} {"time":"2020-09-29T00:00:00Z","pointValue":0} {"time":"2020-09-30T00:00:00Z","pointValue":0}
2.3.3 report data encapsulation
The chart tool used in the front end is EChars, and the data format required for line chart is:
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line' }]
The above format can be in[ https://echarts.apache.org/examples/zh/index.html ]( https://echarts.apache.org/examples/zh/index.html )Inquiry
API interface is defined as follows:
![](images/4-7.png)
![](images/4-6.png)
(1) Define the VO class required for front-end display. The code is as follows:
package com.yikekong.vo; import lombok.Data; import java.io.Serializable; import java.util.List; /** * Line graph encapsulation class */ @Data public class LineVO implements Serializable { private List<String> xdata;//x-axis private List<Long> series;//data }
(2) Add a method call getAlarmTrend in the ReportController to get the data, convert it and return it to the front end
/** * Get alarm trend * @return */ @GetMapping("/trend/{startTime}/{endTime}/{type}") public LineVO getQuotaTrendCollect(@PathVariable String startTime,@PathVariable String endTime,@PathVariable Integer type){ List<TrendPoint> trendPointList = reportService.getAlarmTrend(startTime, endTime, type); LineVO lineVO=new LineVO(); lineVO.setXdata(Lists.newArrayList()); lineVO.setSeries(Lists.newArrayList()); trendPointList.forEach( t->{ lineVO.getXdata().add( t.getTime() ); lineVO.getSeries().add( t.getPointValue().longValue()); }); return lineVO; }
Test:
http://127.0.0.1:9093/report/trend?start=2020-08-01&end=2020-08-31&type=3
2.3.4 time format processing
Requirements:
After the test just now, the date format we returned is 2020-09-01T00:00:00Z. But the format required by the front end is
If it is a minute, the minute value is displayed
If it is an hour, the hour value is displayed
If it is a day, it displays xx month xx day
(1) ReportController adds a new private method to format the date string
/** * Format date string * @param time * @param type * @return */ private String formatTime(String time,int type){ LocalDateTime localTime = LocalDateTime.parse(time,DateTimeFormatter.ISO_OFFSET_DATE_TIME); if(type == 1){ return localTime.getMinute()+""; }else if(type == 2){ return localTime.getHour()+""; }else if(type == 3){ return localTime.getMonthValue()+"month"+localTime.getDayOfMonth()+"day"; } return time; }
(2) Modify getAlarmTrend method and call formatTime to format date
/** * Get alarm trend * @return */ @GetMapping("/trend/{startTime}/{endTime}/{type}") public LineVO getQuotaTrendCollect(@PathVariable String startTime,@PathVariable String endTime,@PathVariable Integer type){ List<TrendPoint> trendPointList = reportService.getAlarmTrend(startTime, endTime, type); LineVO lineVO=new LineVO(); lineVO.setXdata(Lists.newArrayList()); lineVO.setSeries(Lists.newArrayList()); trendPointList.forEach( t->{ lineVO.getXdata().add( formatTime(t.getTime(),type) );//Revise this sentence lineVO.getSeries().add( t.getPointValue().longValue()); }); return lineVO; }
3. Alarm times top10 equipment index report
3.1 demand analysis
The system needs to display the top 10 equipment and indicators with the highest number of alarms in the current hour, the current day and the current 7 days, which are displayed in a histogram. The display effect is as follows:
![](images/3-8.png)
The vertical axis shows the equipment number + indicator name, and the horizontal axis shows the number of alarms, arranged from more to less.
3.2 implementation ideas
(1) Write query statements to realize the top 10 statistics of the number of exceptions. We will learn a new function top here.
(2) Define the specific pojo classes retrieved from the InfluxDB, including the attributes such as device id, indicator id, indicator name, and the number of times counted
(3) Define and obtain the top10 alarm device indicators within a certain time range in the AlarmService service interface and implement them in the implementation class
(4) invoke the method in Service in Controller to get data back to the front end.
3.3 code implementation
3.3.1 alarm times top10 query
(1) First, write a query statement to query the number of alarms in groups according to the device ID and indicator ID
select count(value) as heapValue from quota where alarm='1' and time>='2020-09-01' and time<='2020-09-30' group by deviceId,quotaId,quotaName
Note: in the standard SQL syntax, the grouping condition must appear after the select sentence, while the incluxql does not need to be written like that.
The operation effect of the above statement is as follows:
![](images/4-8.png)
(2) The above result is actually the intermediate result we want. We should also get the top 10 data based on this result. We need to use the top function
select top(heapValue,deviceId,quotaId,quotaName,10) as heapValue from ( select count(value) as heapValue from quota where alarm='1' and time>='2020-09-01' and time<='2020-09-30' group by deviceId,quotaId ) order by desc
The first parameter of the top function is the field to be sorted, the last parameter is the number of results obtained, and the middle parameter is other data to be listed.
The running results of the above statement are as follows:
![](images/4-9.png)
3.3.2 report data query and encapsulation
API design is as follows:
![](images/4-10.png)
(1) Define the pojo class corresponding to top10
package com.yikekong.dto; import lombok.Data; import org.influxdb.annotation.Column; import org.influxdb.annotation.Measurement; import java.io.Serializable; /** * Cumulative index */ @Data @Measurement(name = "quota") public class HeapPoint implements Serializable{ @Column(name = "deviceId") private String deviceId; @Column(name = "heapValue") private Double heapValue; @Column(name = "quotaId") private String quotaId; @Column(name = "quotaName") private String quotaName; }
(2) Define the method of obtaining data in the ReportService service interface
/** * Obtain the equipment index with the largest number of alarms within a certain time range * @return */ List<HeapPoint> getTop10Alarm(String startTime, String endTime);
Implement this method in the ReportServiceImpl implementation class:
@Override public List<HeapPoint> getTop10Alarm(String startTime, String endTime) { StringBuilder sbSql = new StringBuilder("select top(heapValue,deviceId,quotaId,quotaName,10) as heapValue " + " from(select count(value) as heapValue from quota where alarm='1' "); sbSql.append("and time>='"); sbSql.append(startTime); sbSql.append("' and time<='"); sbSql.append(endTime); sbSql.append("' group by deviceId,quotaId) order by desc"); return influxDBRepository.query(sbSql.toString(),HeapPoint.class); }
The specific implementation idea of sql statement here is: the total number of alarms grouped according to equipment and indicators within a certain time range is obtained in sql sub query, and then the top function is used to get the top ten of the result set after reverse order in external query, so as to obtain the data we want.
(3) Add a method to get the top ten data in the ReportController, and call the method of the service layer in this method to return to the front end:
/** * Obtain the equipment index with the highest number of alarms in the top 10 within a certain time range * @param startTime * @param endTime * @return */ @GetMapping("/top10Alarm/{startTime}/{endTime}") public List<HeapPoint> getTop10Alarm(@PathVariable String startTime, @PathVariable String endTime){ return reportService .getTop10Alarm(startTime,endTime); }
4. Custom indicator Kanban
4.1 demand analysis
System managers can add a user-defined Kanban to display data in the system. The user-defined Kanban is the indicator selected by the user and some devices that report the indicator according to the indicator, so as to facilitate tracking and observing the trend of relevant devices under the indicator. The displayed trend is also multi-dimensional, Display the current 60 minute indicators and relevant equipment trends in an hour according to the hour; Display the equipment trend of each hour of the day according to the index 24 hours a day; Display the 24-hour indicator equipment trend in the last 7 days according to 7 days.
The prototype diagram of adding Kanban is as follows:
![](images/4-2.png)
The prototype design of the added Kanban is shown on the home page as follows:
![](images/4-3.png)
The horizontal axis shows the time, and the data on the vertical axis is the index data reported by the equipment for the first time at each time point.
4.2 realization ideas
(1) When adding Kanban in the system, you can only select numerical indicators. Load all numerical indicators from mysql database in pages. Indicators are single choice
(2) After selecting indicators when adding Kanban, you can only select the equipment that has submitted the indicator under the previously selected indicators when selecting equipment. The equipment is multi-choice, and a maximum of 10 equipment can be selected
(3) When providing data to the front end, first query the indicator Id of the panel and all corresponding device numbers from mysql, and obtain the first indicator value of all device indicators in the corresponding time period from InfluxDB according to the time type passed in from the front end
(4) Combine all data into the data format desired by the front end and return
4.3 code implementation
4.3.1 get the list of numerical indicators
![](images/4-11.png)
(1) Load all numerical indicators from mysql database page by page, and define methods in the QuotaService interface
IPage<QuotaEntity> queryNumberQuota(Long page, Long pageSize);
Implement this method in the QuotaServiceImpl implementation class:
@Override public IPage<QuotaEntity> queryNumberQuota(Long page, Long pageSize) { Page<QuotaEntity> pageResult = new Page<>(page,pageSize); LambdaQueryWrapper<QuotaEntity> wrapper = new LambdaQueryWrapper<>(); wrapper .eq(QuotaEntity::getValueType,"Long") .or() .eq(QuotaEntity::getValueType,"Integer") .or() .eq(QuotaEntity::getValueType,"Double"); return this.page(pageResult,wrapper); }
(2) Add an interface to get the data in the QuotaController and call the above method to return the data to the front end
/** * Get numeric indicators by page * @param page * @param pageSize * @return */ @GetMapping("/numberQuota") public Pager<QuotaEntity> queryNumberQuota(@RequestParam(value = "page",required = false,defaultValue = "1") Long page, @RequestParam(value = "pageSize",required = false,defaultValue = "10") Long pageSize){ return new Pager<>(quotaService.queryNumberQuota(page,pageSize)); }
4.3.2 obtain equipment related to an index
(1) Query statement
Due to the large number of devices, we need to use paging query. The statement is as follows:
select distinct(deviceId) as deviceId from(select deviceId,value from quota where quotaId='1' group by deviceId,quotaId) limit 10 OFFSET 0
Write a statement to obtain the number of devices for paging
select count(distinct(deviceId)) as deviceCount from(select deviceId,value from quota where quotaId='' group by deviceId,quotaId)
(2) In the ReportService interface, define the method to obtain the associated device through the indicator ID
/** * Obtain associated equipment through indicators * @param quotaId * @return */ Pager<String> getDeviceByQuota(Long page,Long pageSize,String quotaId);
Implement this method in the ReportServiceImpl implementation class:
@Override public Pager<String> getDeviceByQuota(Long page, Long pageSize, String quotaId) { String fromQl=" from ( select deviceId,value from quota where quotaId='"+ quotaId+"' group by deviceId,quotaId ) "; String listQl="select distinct(deviceId ) as deviceId "+fromQl+" limit "+pageSize +" offset "+(page-1)*pageSize; String countQl=" select count( distinct(deviceId )) as count "+fromQl; List<QuotaInfo> quotaInfoList = influxRepository.query(listQl, QuotaInfo.class); //Device id list List<String> deviceIdList = quotaInfoList.stream().map(quotaInfo -> quotaInfo.getDeviceId()).collect(Collectors.toList()); //Number of statistical records List<QuotaCount> quotaCountList = influxRepository.query(countQl, QuotaCount.class); if( quotaCountList==null || quotaCountList.size()==0 ){ Pager<String> pager=new Pager<String>(0L,0L); pager.setItems(Lists.newArrayList()); return pager; } Long count = quotaCountList.get(0).getCount(); Pager<String> pager=new Pager<String>(count,pageSize); pager.setItems(deviceIdList); return pager; }
(3) Add an interface to obtain the data in the ReportController and call the above method to return the data to the front end:
/** * Query equipment list through indicators * @param quotaId * @return */ @GetMapping("/devices") public Pager<String> getDeviceByQuota( @RequestParam(value = "page",required = false,defaultValue = "1") Long page, @RequestParam(value = "pageSize",required = false,defaultValue = "10") Long pageSize, @RequestParam(value = "quotaId")String quotaId){ return reportService.getDeviceByQuota(page,pageSize,quotaId); }
4.3.3 query index value sequence by equipment index
(1) Write query statements
select first(value) as pointValue from quota where time>='2020-09-01' and time<='2020-09-30' and quotaId='1' and deviceId='100001' group by time(1d)
In order to view the running effect more conveniently, we need to execute precision rfc3339 to set the time display format
The results are as follows:
![](images/4-12.png)
(2) Write encapsulation class
package com.yikekong.dto; import lombok.Data; import org.influxdb.annotation.Column; import org.influxdb.annotation.Measurement; import java.io.Serializable; /** * Encapsulates the data of line graph class results */ @Data @Measurement(name = "quota") public class TrendPoint2 implements Serializable { @Column(name = "time") private String time;//time @Column(name = "pointValue") private Double pointValue;//Point in time data }
(3) Define the method to obtain the trend indicator point set under a certain indicator within a period of time in the ReportService interface
/** * Get indicator trends * @param start Start time yyyy MM DD HH: mm: SS * @param end End time yyyy MM DD HH: mm: SS * @param quotaId Indicator Id * @param type Time statistics type (within 1:60 minutes, 2:24 hours on the same day, 3:7 days) * @param deviceId Equipment code * @return */ List<TrendPoint2> getQuotaTrend( String start, String end, String quotaId,String deviceId,int type);
Implement this method in the ReportServiceImpl implementation class
@Override public List<TrendPoint2> getQuotaTrend(String startTime, String endTime, String quotaId, String deviceId, int type) { StringBuilder ql=new StringBuilder("select first(value) as pointValue from quota "); ql.append("where time>='"+ startTime+"' and time<='"+ endTime +"' " ); ql.append("and quotaId='"+quotaId +"' "); ql.append("and deviceId='"+ deviceId +"' "); if(type==1){ //1 hour ql.append("group by time(1m)"); } if(type==2){ //1 day ql.append("group by time(1h)"); } if(type==3){ //7 days ql.append("group by time(1d)"); } List<TrendPoint2> trendPoint2List = influxRepository.query(ql.toString(), TrendPoint2.class); return trendPoint2List; }
Unit test:
@Test public void testQuotaTrend(){ List<TrendPoint2> trendPoint2List = reportService.getQuotaTrend("2020-09-01 00:00:00", "2020-09-30 23:59:59" , "1", "100001", 3); for( TrendPoint2 trendPoint2:trendPoint2List ){ try { System.out.println( JsonUtil.serialize(trendPoint2) ); } catch (JsonProcessingException e) { e.printStackTrace(); } } }
The test results are as follows:
![](images/4-13.png)
4.3.4 vacancy data completion
Through the above unit test results, we found that many time nodes have no values. If it is null, how should we display it in the graph? Press 0? Obviously unreasonable. For example, for temperature, the meaning of temperature 0 is obviously different from that of no temperature data!
So let's process the vacancy data here. The processing rules are as follows:
If the value of a node is null, it is set to the previous non null value
If the first value of the sequence is null, all values from the first value to the first non null value are the first non null value.
The code is written as follows:
(1) ReportServiceImpl new private method
/** * Fill data * @param trendPoint2List * @return */ private List<TrendPoint2> replenish(List<TrendPoint2> trendPoint2List){ Double previousValue=null;// Previous value for(TrendPoint2 trendPoint2: trendPoint2List ){ if(trendPoint2.getPointValue()!=null){ previousValue=trendPoint2.getPointValue(); break; } } if(previousValue==null){ previousValue=0d; } //Data filling logic for( TrendPoint2 trendPoint2: trendPoint2List){ if(trendPoint2.getPointValue()==null){ trendPoint2.setPointValue(previousValue); } previousValue=trendPoint2.getPointValue(); } return trendPoint2List; }
(2) Modify the return value of getQuotaTrend method
return replenish(trendPoint2List);
4.3.5 report data preview
The format required by the front end is as follows:
{ "xdata": [ "8 January 1", "8 February 2", "8 March 3" ], "series": [ { "name": "100001", "data": [ 70, 90, 100 ] }, { "name": "100002", "data": [ 50, 60, 80, ] } ], "name": "Panel name" }
(1) Define vo the data sequence used to encapsulate an indicator
package com.yikekong.vo; import lombok.Data; import java.io.Serializable; import java.util.List; /** * Panel index data */ @Data public class BoardQuotaData implements Serializable{ /** * Name (equipment number) */ private String name; /** * Index data */ private List<Double> data; }
(2) Define vo, which is used to encapsulate the data structure of the whole report
package com.yikekong.vo; import lombok.Data; import java.io.Serializable; import java.util.List; /** * Panel VO object */ @Data public class BoardQuotaVO implements Serializable{ /** * x Axis data */ private List<String> xdata; /** * Y Axis data */ private List<BoardQuotaData> series; /** * Panel name */ private String name; }
(3) Define the method of obtaining panel indicator data in the ReportService interface
/** * Indicator trend chart * @param quotaId * @param deviceIds * @param startTime * @param endTime * @param type * @return */ BoardQuotaVO getBoardData(String quotaId,List<String> deviceIds,String startTime,String endTime,Integer type );
Implement this method in the ReportServiceImpl implementation class
@Override public BoardQuotaVO getBoardData(String quotaId, List<String> deviceIds, String startTime, String endTime, Integer type) { //Parameter verification if( quotaId==null || deviceIds==null || deviceIds.size()==0 ){ return new BoardQuotaVO(); } BoardQuotaVO boardQuotaVO=new BoardQuotaVO(); boardQuotaVO.setSeries( Lists.newArrayList()); for( String deviceId:deviceIds ){ //Cycle each device //Index trend of each equipment List<TrendPoint2> trendPoint2List = getQuotaTrend(startTime, endTime, quotaId, deviceId, type); //x-axis if(boardQuotaVO.getXdata()==null){ boardQuotaVO.setXdata( trendPoint2List.stream().map(trendPoint2 -> trendPoint2.getTime() ).collect( Collectors.toList() ) ); } //data BoardQuotaData boardQuotaData=new BoardQuotaData(); boardQuotaData.setName( deviceId ); boardQuotaData.setData( trendPoint2List.stream().map( trendPoint2 -> trendPoint2.getPointValue() ).collect(Collectors.toList()) ); boardQuotaVO.getSeries().add(boardQuotaData); } return boardQuotaVO; }
(4) Build vo to preview report incoming parameters
package com.yikekong.vo; import lombok.Data; import java.util.List; @Data public class PreviewVO { private String quotaId; private List<String> deviceIdList; private Integer type; private String start; private String end; }
(5) Call the method in the ReportController to return the data to the front end
/** * Report Preview * @param previewVO * @return */ @PostMapping("/preview") public BoardQuotaVO getPreviewData( @RequestBody PreviewVO previewVO ){ BoardQuotaVO boardData = reportService.getBoardData( previewVO.getQuotaId(), previewVO.getDeviceIdList(), previewVO.getStart(), previewVO.getEnd(), previewVO.getType()); //Time processing List<String> xdata=Lists.newArrayList(); for(String x:boardData.getXdata()){ xdata.add(formatTime(x,previewVO.getType() )) ; } boardData.setXdata(xdata); return boardData; }