该项目为前后端分离的基础项目,业务重点围绕设备管理、故障管理、保养管理、关注管理、设备运行数据管理等模块,而我主要负责故障管理、设备运行数据管理模块,下面我细讲一下设备运行数据管理中使用效率管理是怎么实现的。
一.系统需求分析
5.2使用效率管理
展示所有设备使用平均,使用效率计算方法:绿灯使用时间/(绿灯+黄灯+红灯时间)列表包含:日期、设备编号、设备名称、当天使用效率(绿灯/24)、累计使用效率(每天凌晨累加当天的使用效率)。
二、系统开发的配置
1.开发环境版本:JDK 8
2.开发计算机系统:windows
3.开发工具:后端----------IDEA
前端----------Visual Studio Code
数据库-------Navicat 15 for MySQL
4.框架使用:前端-----------vue+element ui等
后端-----------springboot+mybatis/mybatis-plus+springmvc等
三、项目部分模块的架构
1.数据库
计算设备当天使用效率,需要两张表分别为设备表、设备状态记录表。
设备表的设计如下:
设备状态记录表的设计如下:
2.后端包层
3.mapper的xml
4.前端的页面
在后端把计算出来,并赋给设备表中day_efficiency字段,然后在前端展示。
四、具体实现
1.实体类
DevicesEntity设备实体
package com.wedu.modules.xxx_supervise.entity;
import cn.hutool.core.annotation.Alias;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
//@TableName
@Data
@TableName("devices")
public class DevicesEntity implements Serializable {
private static final long serialVersionUID = 1L;
//车间
@Alias("车间")
private String workshopName;
// 设备类型 0:其他设备 1:生产设备
@Alias("设备型号")
private Integer deviceType;
//设备编号(主键)
@TableId(type = IdType.AUTO)
@Alias("设备ID")
private Long deviceId;
//设备名
@Alias("设备名称")
private String deviceName;
@Alias("设备型号")
//设备型号
private String deviceModel;
//制造厂
@Alias("制造厂")
private String manufacturer;
//生产日期
@Alias("生产日期")
private Date productionTime;
//开始使用日期
@Alias("开始使用时间")
private Date useTime;
//维保到期
@Alias("保养到期时间")
private Date maintenanceTime;
//保养周期
@Alias("保养周期(天)")
private Integer maintenancePeriod;
//备注
@Alias("备注")
private String remark;
//当日的效率
@Alias("当日的效率 ")
private double dayEfficiency;
//累计的效率
@Alias("累计的效率 ")
private double totalEfficiency;
//传感器ID
@Alias("传感器ID")
private String sensorId;
//预警周期
@Alias("预警周期(天)")
private Integer forewarningPeriod;
//预警状态
@Alias("预警状态(0未预警 1预警 2预警过期)")
private Integer forewarningStatus;
//设备状态 0:正常 1:保养 2:故障 3:维修中 4:保养中
private Integer status;
//逻辑删除 当使用该注解时,MP会自动将删除逻辑改为更新逻辑
@TableLogic(value = "0", delval = "1")
private Integer isDeleted;
//创建时间
@Alias("创建时间")
private Date createTime;
//创建者id
@Alias("创建者id")
private Long createUserId;
//更新时间
@Alias("更新时间")
private Date updateTime;
//更新者id
@Alias("更新者id")
private Long updateUserId;
//红灯开始时间
@Alias("红灯开始时间")
private Date redFistTime;
//故障开始时间
public Integer setStatus(Integer status) {
this.status = status;
if (status !=null && status==2){
this.setRedFistTime(new Date());
}
return status;
}
//当前用户是否关注此设备
@TableField(exist = false)
private Integer attentionStatus;//设备实体类新增属性
//数量
@TableField(exist = false)
private long dataCount;
}
DevicesStateEntity设备状态记录
package com.wedu.modules.xxx_supervise.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
//@TableName
@Data
@TableName("devices_state")
public class DevicesStateEntity implements Serializable {
private static final long serialVersionUID = 1L;
//设备状态记录id
@TableId(type = IdType.AUTO)
private Long id;
//设备编号
private Long deviceId;
//灯开始时间
private Date lightFistTime;
//灯结束时间
private Date lightEndTime;
//灯的状态
private Integer status;
//红灯时长
private String redMaintainTime;
//红灯时长标准为秒
private Long redMaintainTimeS;
//黄灯时长
private String yMaintainTime;
//黄灯时长标准为秒
private Long yMaintainTimeS;
//逻辑删除 当使用该注解时,MP会自动将删除逻辑改为更新逻辑
@TableLogic(value = "0", delval = "1")
private Integer isDelete;
//创建时间
private Date createTime;
//创建者id
private Long createBy;
//更新时间
private Date updateTime;
//更新者id
private Long updateBy;
//总绿灯时长
@TableField(exist = false)
private String greenLightTime;
//总红灯时长
@TableField(exist = false)
private String redLightTime;
//总黄灯时长
@TableField(exist = false)
private String yellowLightTime;
//设备表的id
@TableField(exist = false)
private Long dId;
//设备名
@TableField(exist = false)
private String deviceName;
@TableField(exist = false)
private Integer dataNum;
@TableField(exist = false)
private Long d1Id;
}
2.在业务逻辑层的RuningDataServiceImpl中计算当天绿灯的效率
(一)在DevicesService接口中创建queryPageAllStateRecord()方法和RuningDataService接口中创建DeviceDayEfficiency()方法
/**
* 所有设备当天的使用效率
* @param params
* @return
*/
PageUtils queryEfficiencyPage(Map<String, Object> params);
/**
* 设备当天的使用效率
* @param
*/
void DeviceDayEfficiency();
(二)在RuningDataServiceImpl类中创建DeviceDayEfficiency()方法计算绿灯的效率
/**
* 计算设备当天的使用效率
* @param
*/
@Override
public void DeviceDayEfficiency() {
(三)在DevicesStateDao.xml中创建两个查询
查询设备表中的所有设备在设备状态记录有多少条数据
<!--查询设备表中的所有设备在设备状态记录有多少条数据-->
<select id="countDevicesState" resultType="com.wedu.modules.wzh_supervise.entity.DevicesStateEntity">
SELECT d.device_id d1_id, COUNT(ds.device_id) AS dataNum
FROM devices d
LEFT JOIN devices_state ds ON ds.device_id = d.device_id
AND ds.is_delete = 0
AND DATE(ds.light_fist_time) = CURDATE()
WHERE d.is_deleted = 0
GROUP BY d.device_id
</select>
根据设备id查询该设备id在设备状态记录表中当天所有记录
<!--根据设备id查询该设备id在设备状态记录表中当天所有记录-->
<select id="AllDevicesStateBydeviceId" resultType="com.wedu.modules.wzh_supervise.entity.DevicesStateEntity">
SELECT device_id,light_fist_time , status
FROM devices_state ds
where DATE(ds.light_fist_time) = CURDATE() and ds.device_id=#{deviceId}
order by device_id , light_fist_time
</select>
(四)在DevicesStateDao中接收这两个查询,并封装到集合中
List<DevicesStateEntity> countDevicesState();
List<DevicesStateEntity> AllDevicesStateBydeviceId(Long deviceId);
(五)在DeviceDayEfficiency()方法中首先调用devicesStateDao类中countDevicesState()方法当天获取设备表的所有设备在设备状态记录表中有多少条数据,然后判断当天获取设备表的哪个设备在设备状态记录表中没有记录,如果没有记录就只有两种情况1.该设备在当天是一致是正常的2.该设备在当天是一致是不正常的,那么一致正常其效率为1,一致不正常其效率为0;
/*当天获取设备表的所有设备在设备状态记录表中有多少条数据*/
//用sql语句多表联查,获取数据装到一个集合里
List<DevicesStateEntity> xxx = devicesStateDao.countDevicesState();
//遍历集合
for (DevicesStateEntity devicesStateEntity : xxx) {
/*当天获取设备表的哪个设备在设备状态记录表中没有记录*/
if (devicesStateEntity.getDataNum() == 0) {
//那就根据设备id把该设备查出来
QueryWrapper<DevicesEntity> queryWrapper1 = new QueryWrapper<>();
queryWrapper1.eq("device_id", devicesStateEntity.getD1Id());
DevicesEntity devicesEntity1 = devicesDao.selectOne(queryWrapper1);
//如果该设备的状态为正常
if (devicesEntity1.getStatus() == 0) {
//证明这一天都是正常的,其效率为100%
devicesEntity1.setDayEfficiency(1);
//如果该设备的状态不是正常的
} else {
//证明这一天的效率状态不正常效率为0%
devicesEntity1.setDayEfficiency(0);
}
//修改该设备
devicesDao.updateById(devicesEntity1);
}
(六)如果该设备在状态记录表有数据,根据其设备id,把它在设备状态记录中的所有记录获取,然后这些数据放到集合中在调用calculateGreenLightDuration()方法计算其设备在当天的绿灯时长,将绿灯时长/24,最后将其值传到设备表中day_efficiency字段中
else if (devicesStateEntity.getDataNum() != 0) {
//根据其设备id,把它在设备状态记录中的所有记录获取
List<DevicesStateEntity> mys = devicesStateDao.AllDevicesStateBydeviceId(devicesStateEntity.getD1Id());
System.out.println(mys);
// 解析数据
// 创建用于存储DeviceStatusRecord对象的集合
List<DevicesStateEntity> deviceStatusRecords = new ArrayList<>();
// 遍历mys列表
for (DevicesStateEntity record : mys) {
// record.getLinghtFistTime()返回LocalDateTime
// 创建DeviceStatusRecord对象并添加到集合中
record.getDeviceId();
record.getLightFistTime();//直接传递record.getLinghtFistTime()
record.getStatus();
/*把DevicesStateEntity中的设备id、灯开始时间、设备状态添加到一个集合中*/
deviceStatusRecords.add(record);
System.out.println(deviceStatusRecords);
}
/*计算其设备在当天的效率*/
//计算其设备在当天绿灯
long greenLightDuration = calculateGreenLightDuration(deviceStatusRecords);
double greenLightDurationInHours = greenLightDuration / (60.0 * 60 * 1000); // 转换为小时
System.out.printf("当天绿灯的时长为:%.2f小时\n", greenLightDurationInHours);
/*根据设备id把其设备获取*/
QueryWrapper<DevicesEntity> queryWrapper1 = new QueryWrapper<>();
queryWrapper1.eq("device_id",devicesStateEntity.getD1Id());
DevicesEntity devicesEntity1 = devicesDao.selectOne(queryWrapper1);
//将计算好的效率赋给设备表中效率字段
// 保留两位小数不进位
devicesEntity1.setDayEfficiency( Math.floor(greenLightDurationInHours/24 * 100) / 100);
//更新设备表
devicesDao.updateById(devicesEntity1);
(七)下面是计算绿灯时长所用的方法
//返回当天的当天的0点,格式为xxxx-xx-xx 00:00:00
public static Date getStartOfDay() {
// 创建一个SimpleDateFormat对象,用于格式化日期为"yyyy-MM-dd HH:mm:ss"的格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 获取当前时间的Calendar实例
Calendar calendar = Calendar.getInstance();
// 将Calendar的小时设置为0,表示午夜
calendar.set(Calendar.HOUR_OF_DAY, 0);
// 将Calendar的分钟设置为0
calendar.set(Calendar.MINUTE, 0);
// 将Calendar的秒设置为0
calendar.set(Calendar.SECOND, 0);
// 将Calendar的毫秒设置为0
calendar.set(Calendar.MILLISECOND, 0);
// 获取Calendar对象所表示的时间
Date start = calendar.getTime();
// 使用SimpleDateFormat对象将Date对象格式化为字符串,并打印出来
System.out.println("Start of the day: " + sdf.format(start));
// 返回当天0点的Date对象
return start;
}
//返回当天的当天的23点,格式为xxxx-xx-xx 23:59:59
public static Date getEndOfDay() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
Date end = calendar.getTime();
System.out.println("Start of the day: " + sdf.format(end));
return end;
}
//计算当天绿灯
private long calculateGreenLightDuration(List<DevicesStateEntity> trafficDataList) {
// 初始化变量来存储总绿灯时间
long greenLightDuration = 0;
// 获取一天的开始时间
Date start = getStartOfDay();
// 获取一天的结束时间
Date end = getEndOfDay();
/*如果只有一条数据,其状态为0,那么绿灯时间是从状态为0的时间到一天结束,
其状态不为0,那么绿灯时间是从一天开始时间到状态不为0的时间*/
// 检查trafficDataList中是否只有一个数据项
if (trafficDataList.size() == 1) {
// 获取数据项
DevicesStateEntity data = trafficDataList.get(0);
// 检查状态是否为0(绿灯)
if (data.getStatus() == 0) {
// 计算绿灯持续时间直到一天结束
greenLightDuration = end.getTime() - data.getLightFistTime().getTime();
} else {
// 计算绿灯持续时间从一天开始
greenLightDuration = data.getLightFistTime().getTime() - start.getTime();
}
/*如果数据是多条的,其状态2-0-2-0-2-0,
如果第一个状态为2,证明从0点开始到第一个状态为2,
其时间是为绿灯时间的。如果最后个状态为0,
证明从最后状态为0的时间到这一天结束,
其时间也是绿灯时间的,
那么绿灯时间是从一天开始到第一个状态2的时间加上期间状态从0变2的时间加上最后状态0的时间到一天结束的时间*/
/*如果数据是多条的,其状态0-2-0-2,那么绿灯时间是所有状态从0变2的时间相加*/
} else {
// 处理trafficDataList中的多个数据项
// 检查第一个数据项的状态
if (trafficDataList.get(0).getStatus() == 2 || trafficDataList.get(0).getStatus() == 1) {
// 计算从一天开始到第一个绿灯的持续时间
greenLightDuration += trafficDataList.get(0).getLightFistTime().getTime() - start.getTime();
}
// 遍历剩下的数据项
for (int i = 0; i < trafficDataList.size() - 1; i++) {
DevicesStateEntity current = trafficDataList.get(i);
DevicesStateEntity next = trafficDataList.get(i + 1);
// 检查当前状态是否为绿灯并且下一个状态是黄灯或红灯
if (current.getStatus() == 0 && (next.getStatus() == 2||next.getStatus() == 1)) {
// 计算当前状态和下一个状态变化之间的绿灯持续时间
greenLightDuration += next.getLightFistTime().getTime() - current.getLightFistTime().getTime();
}
}
// 检查最后一个数据项的状态
if (trafficDataList.get(trafficDataList.size() - 1).getStatus() == 0) {
// 计算从最后一个绿灯到一天结束的持续时间
greenLightDuration += end.getTime() - trafficDataList.get(trafficDataList.size() - 1).getLightFistTime().getTime();
}
}
// 返回总绿灯时间
return greenLightDuration;
}
(八)在数据库添加一事件,实现数据库每天每天凌晨把day_efficiency字段清零,然后把day_efficiency加到total_efficiency中。
-- 创建每天凌晨更新累计效率和清空当日效率
DROP EVENT IF EXISTS evt_reset_daily_efficiency;
CREATE EVENT evt_reset_daily_efficiency
ON SCHEDULE EVERY 1 DAY
STARTS TIMESTAMP(CURRENT_DATE, '00:00:00') -- 设置开始时间为今天凌晨
DO
BEGIN
UPDATE devices
SET day_efficiency = 0,
total_efficiency = total_efficiency + COALESCE(day_efficiency, 0);
END;
(九)在DevicesServiceImpl在重写queryEfficiencyPage()展示效率列表。
/**
* 效率列表
* 分页和模糊查询
* @param params 框架自动把url参数或者表单参数封装为map
* @return
*/
@Override
public PageUtils queryEfficiencyPage(Map<String, Object> params) {
//从请求参数中获取设备名
String deviceName = (String) params.get("deviceName");
IPage<DevicesEntity> page = this.page(
new Query<DevicesEntity>().getPage(params),
new QueryWrapper<DevicesEntity>()
.like(StringUtils.isNotBlank(deviceName), "device_name", deviceName)
.orderByDesc("update_time")
);
return new PageUtils(page);
}
3. 在控制层RuningDataController类,调用DeviceDayEfficiency(),queryEfficiencyPage(params)这两个方法。
/**
* 效率列表
* 分页和模糊查询
* @param params 框架自动把url参数或者表单参数封装为map
* @return
*/
@GetMapping("/efficiency/Alllist")
public R queryAllDevicesEfficiency(@RequestParam Map<String, Object> params) {
//计算当天效率
runingDataService.DeviceDayEfficiency();
PageUtils page = devicesService.queryEfficiencyPage(params);
return R.ok().put("page", page);
}
4.在前端把该页面展示出来
<template>
<div class="mod-devices">
<!-- 运行状态管理-->
<!-- 查询表单 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="search()">
<el-form-item>
<el-input v-model="dataForm.deviceName" placeholder="请输入设备名" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="search()">查询</el-button>
</el-form-item>
</el-form>
<!-- 数据展示列表 -->
<el-table :data="dataList" border v-loading="dataListLoading"
style="width: 100%;">
<el-table-column prop="deviceId" header-align="center" align="center" label="设备ID"></el-table-column>
<el-table-column prop="deviceName" header-align="center" align="center" label="设备名"></el-table-column>
<el-table-column prop="status" header-align="center" align="center" label="运行状态">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 0" size="small">正常</el-tag>
<el-tag v-else-if="scope.row.status === 1" size="small" type="warning">保养</el-tag>
<el-tag v-else-if="scope.row.status === 4" size="small" type="warning">保养中</el-tag>
<el-tag v-else-if="scope.row.status === 3" size="small" style="background-color: #a78ba7; color: #7918a0 ;">维修中</el-tag>
<el-tag v-else size="small" type="danger">故障</el-tag>
</template>
</el-table-column>
<el-table-column prop="efficiency" header-align="center" align="center" label="当天使用效率">
<template slot-scope="scope">
<el-text >{{(scope.row.dayEfficiency)*100}}%</el-text>
</template>
</el-table-column>
<el-table-column header-align="center" align="center" label="积累使用效率">
<template slot-scope="scope">
<el-text >{{(scope.row.totalEfficiency)*100}}%</el-text>
</template>
</el-table-column>
</el-table>
<!-- 分页工具条 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex"
:page-sizes="[10, 20, 30, 40]" :page-size="pageSize" :total="totalPage"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
</div>
</template>
<script>
export default {
// 数据域
data () {
return {
// 查询表单数据模型
dataForm: {
faultManifestation: ''
},
// 当前页码
pageIndex: 1,
// 每页记录数
pageSize: 10,
// 总页数
totalPage: 0,
// 设备数据列表
dataList: [],
// 批量删除数组 数组元素为设备对象
dataListSelections: [],
// 是否显示加载动效
dataListLoading: false
}
},
// 声明组件
components: {
},
// 生命周期函数
mounted () {
this.getDataList()
},
// 修改状态
methods: {
// 每页记录数
sizeChangeHandle (val) {
this.pageSize = val
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 查询表单的查询按钮以及回车事件绑定
search () {
// this.pageIndex = 1;
/* alert(this.dataForm.goodsName) */
this.getDataList()
},
getDataList () {
// 发送查询请求
this.$http({
url: this.$http.adornUrl('/xxx/runingData/efficiency/Alllist'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'deviceName': this.dataForm.deviceName
// 'status': this.dataForm.status
})
}).then(resp => {
/* console.log(resp); */
let code = resp.data.code
if (!code && resp.data.code === 0) {
this.dataList = resp.data.page.list
this.totalPage = resp.data.page.totalCount
}
})
}
}
}
</script>