package com.dy.rtuMw.server.upgrade;
|
|
import com.alibaba.fastjson2.annotation.JSONField;
|
import com.dy.common.softUpgrade.parse.HexFileParse;
|
import com.dy.common.softUpgrade.state.UpgradeRtu;
|
import com.dy.common.softUpgrade.state.UpgradeState;
|
import com.dy.common.softUpgrade.state.UpgradeTaskVo;
|
import com.dy.common.util.Callback;
|
import com.dy.common.util.DateTime;
|
import lombok.Data;
|
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.Logger;
|
|
import java.util.*;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
/**
|
* @Author: liurunyu
|
* @Date: 2024/11/4 14:52
|
* @Description
|
*/
|
@Data
|
public class UpgradeTask {
|
|
private static final Logger log = LogManager.getLogger(UpgradeTask.class.getName());
|
|
protected static final String TaskOverType_Natural = "自然" ;
|
protected static final String TaskOverType_Force = "强制" ;
|
|
@JSONField(serialize = false)
|
protected Integer failTryTimes ;//升级失败后,重新偿试升级次数,0表示不重新偿试升级
|
@JSONField(serialize = false)
|
protected Integer ugMaxRtuSameTime ;//同时升级RTU最大个数
|
|
public String setupDt ;//设置时间(yyyy-mm-dd HH:MM:SS)
|
@JSONField(serialize = false)
|
private Long setupDtLong ;//设置时间
|
|
public UpgradeTaskVo taskVo ;//升级任务值对象
|
|
@JSONField(serialize = false)
|
protected byte[][] softFileDataGrp ;//以512字节为单位把升级程序数据分组
|
|
@JSONField(serialize = false)
|
protected ConcurrentHashMap<String, UpgradeRtu> upgradeRtus;//升级状态
|
|
public boolean taskIsOver = false ;//任务是否完成
|
public String taskOverType = "" ;//任务完成方式(自然,强制)
|
public String taskOverDt = "" ;//任务完成时间(yyyy-mm-dd HH:MM:SS)
|
|
///////////////////////////////////////////////////
|
//以下内部控制用
|
@JSONField(serialize = false)
|
private int curUgRunningRtuTotal = 0 ;//当前正在升级的RTU个数
|
|
public UpgradeTask() {
|
this.curUgRunningRtuTotal = 0 ;
|
}
|
/**
|
* 初始化配置信息
|
*/
|
public void initOption(Integer failTryTimes, Integer ugMaxRtuSameTime) {
|
this.failTryTimes = failTryTimes;
|
this.ugMaxRtuSameTime = ugMaxRtuSameTime;
|
}
|
/**
|
* 设置升级任务
|
* @param taskVo UpgradeTaskVo 升级任务对象
|
* @throws Exception
|
*/
|
public void setTask(UpgradeTaskVo taskVo) throws Exception {
|
if(taskVo.id == null || taskVo.id.trim().length() == 0){
|
throw new Exception("升级任务id必须提供") ;
|
}
|
if(taskVo.softFileName == null || taskVo.softFileName.trim().length() == 0){
|
throw new Exception("升级软件(hex)文件名称必须提供") ;
|
}
|
if(taskVo.softStoreAddr == null || taskVo.softStoreAddr.trim().length() != 8){
|
throw new Exception("升级程序存放地址不合法,必须是8字符(十六进制)的字符串") ;
|
}
|
if(taskVo.softStartAddr == null || taskVo.softStartAddr.trim().length() != 8){
|
throw new Exception("程序覆盖起始地址不合法,必须是8字符(十六进制)的字符串") ;
|
}
|
if(taskVo.softFileData64 == null || taskVo.softFileData64.trim().length() == 0){
|
throw new Exception("升级程序内容必须提供") ;
|
}
|
if(taskVo.softBytesCalculate == null){
|
throw new Exception("公式计算升级程序有效序字节数必须提供") ;
|
}
|
if(taskVo.softByteSrc16 == null){
|
throw new Exception("有效升级程序字节数CRC16校验值必须提供") ;
|
}
|
if(taskVo.rtuAddrList == null || taskVo.rtuAddrList.size() <= 0){
|
throw new Exception("升级设备RTU地址必须提供") ;
|
}
|
if(taskVo.callbackWebUrl == null || taskVo.callbackWebUrl.trim().equals("")){
|
throw new Exception("回调网址必须提供") ;
|
}
|
this.setupDt = DateTime.yyyy_MM_dd_HH_mm_ss() ;
|
this.setupDtLong = System.currentTimeMillis() ;
|
this.taskVo = taskVo ;
|
|
this.upgradeRtus = new ConcurrentHashMap<>();
|
if(taskVo.softFileData64 != null && !taskVo.softFileData64.trim().equals("")){
|
taskVo.softFileData = Base64.getDecoder().decode(taskVo.softFileData64);
|
List<byte[]> listBytes = new HexFileParse().splitBytesByUnit512(taskVo.softFileData);
|
this.softFileDataGrp = listBytes.toArray(new byte[0][]);
|
try{
|
for(String rtuAddr : this.taskVo.rtuAddrList){
|
//此时状态设置成离线状态
|
UpgradeRtuDev ugRtu = new UpgradeRtuDev(this.taskVo, this.failTryTimes, rtuAddr, this.softFileDataGrp.length, UpgradeRtuDev.STATE_OFFLINE) ;
|
this.upgradeRtus.put(rtuAddr, ugRtu) ;
|
}
|
}catch (Exception e){
|
log.error(e);
|
}
|
}
|
}
|
/**
|
* RTU有上行数据了,触发下发升级数据
|
* @param rtuAddr
|
* @param code
|
* @param callbackCom
|
*/
|
public void trigger(String rtuAddr, String code, String protocolName, Short protocolVersion, Callback callbackCom){
|
if(this.upgradeRtus != null && this.upgradeRtus.size() > 0
|
&& this.taskVo.rtuAddrList != null && this.taskVo.rtuAddrList.size() > 0){
|
UpgradeRtu ugRtu = this.upgradeRtus.get(rtuAddr) ;
|
if(ugRtu == null){
|
//根据方法setTask的逻辑,只要RTU在升级之列,此处ugRtuState一定不为null
|
//为保险,实现下面逻辑
|
if(taskVo.rtuAddrList.contains(rtuAddr)){
|
ugRtu = new UpgradeRtuDev(this.taskVo, this.failTryTimes, rtuAddr, this.taskVo.softFileData.length) ;
|
upgradeRtus.put(rtuAddr, ugRtu) ;
|
}else{
|
//rtu不在升级之列
|
return ;
|
}
|
}
|
if(ugRtu != null){
|
if(ugRtu.isOver){
|
//当前RTU已经升级完成,无需再升级
|
return;
|
}else{
|
if(UpgradeUnit.confVo.ugMaxRtuAtOnce > 0){
|
//设置了同时升级的RTU最大数量的限制
|
if(ugRtu.state == UpgradeRtuDev.STATE_OFFLINE){
|
//初始态,说明升级任务设置以来,该RTU第一次上行数据
|
if(this.curUgRunningRtuTotal <= UpgradeUnit.confVo.ugMaxRtuAtOnce){
|
//当前正在升级的RTU数量还未受限
|
ugRtu.trigger(code, protocolName, protocolVersion, this.softFileDataGrp, callbackCom) ;
|
}else{
|
//同时升级的RTU数量受限,等待下次机会
|
//但先表明一下状态
|
ugRtu.state = UpgradeRtuDev.STATE_UNSTART ;
|
return ;
|
}
|
}else if(ugRtu.state == UpgradeRtuDev.STATE_UNSTART){
|
//根据上面逻辑, 说明必然在线了
|
if(this.curUgRunningRtuTotal <= UpgradeUnit.confVo.ugMaxRtuAtOnce){
|
//当前正在升级的RTU数量还未受限
|
ugRtu.trigger(code, protocolName, protocolVersion, this.softFileDataGrp, callbackCom) ;
|
}else{
|
//同时升级的RTU数量受限,等待下次机会
|
return ;
|
}
|
}else{
|
//RTU已经处于升级过程中
|
ugRtu.trigger(code, protocolName, protocolVersion, this.softFileDataGrp, callbackCom) ;
|
}
|
}else{
|
//没有设置同时升级的RTU最大数量的限制
|
ugRtu.trigger(code, protocolName, protocolVersion, this.softFileDataGrp, callbackCom) ;
|
}
|
}
|
}else{
|
//rtu不在升级之列
|
return ;
|
}
|
}
|
}
|
|
/**
|
* 强制结束升级任务
|
*/
|
public void forceOver(){
|
if(!this.taskIsOver){
|
this.taskIsOver = true ;//强制设置任务完成
|
this.taskOverType = TaskOverType_Force ;//任务完成方式(自然,强制)
|
this.taskOverDt = DateTime.yyyy_MM_dd_HH_mm_ss() ;//任务完成时间(yyyy-mm-dd HH:MM:SS)
|
//this.taskVo.rtuAddrList.clear();
|
//this.upgradeState.clear();
|
}
|
}
|
|
/**
|
* 当前升级状态
|
* @return
|
*/
|
public UpgradeState currentUpgradeState() {
|
UpgradeState state = new UpgradeState() ;
|
if(this.taskVo.rtuAddrList != null && this.taskVo.rtuAddrList.size() > 0){
|
state.rtuTotal = this.taskVo.rtuAddrList.size() ;
|
if(this.upgradeRtus != null && this.upgradeRtus.size() > 0){
|
AtomicBoolean hasRunning = new AtomicBoolean(false);
|
this.upgradeRtus.values().stream().forEach(info ->{
|
if(info.state == UpgradeRtu.STATE_OFFLINE){
|
state.offLineTotal ++ ;
|
}else if(info.state == UpgradeRtu.STATE_UNSTART){
|
state.unStartTotal ++ ;
|
}else if(info.state == UpgradeRtu.STATE_RUNNING){
|
state.runningTotal ++ ;
|
}else if(info.state == UpgradeRtu.STATE_SUCCESS) {
|
state.successTotal++;
|
}else if(info.state == UpgradeRtu.STATE_FAIL) {
|
state.failTotal++;
|
}else if(info.state == UpgradeRtu.STATE_FAILONE) {
|
state.failOneTotal++;
|
state.failTotal++;
|
}else if(info.state == UpgradeRtu.STATE_FAILOFFLINE) {
|
state.failTotal++;
|
state.failOffTotal++;
|
}
|
if(info.isOver){
|
state.overTotal++;
|
}else{
|
hasRunning.set(true);
|
}
|
});
|
if(!hasRunning.get()){
|
state.allOver = true ;
|
}else{
|
state.allOver = false ;
|
}
|
}
|
}
|
return state ;
|
}
|
|
/**
|
* Rtu升级信息
|
* @param rtuAddr
|
* @return
|
*/
|
public UpgradeRtu upgradeInfos(String rtuAddr){
|
return this.upgradeRtus.get(rtuAddr) ;
|
}
|
|
/**
|
* 一些Rtu升级信息
|
* @param rtuAddrList
|
* @return
|
*/
|
public List<UpgradeRtu> upgradeInfos(List<String> rtuAddrList){
|
List<UpgradeRtu> list = new ArrayList<>() ;
|
for(String rtuAddr : rtuAddrList){
|
UpgradeRtu info = this.upgradeRtus.get(rtuAddr) ;
|
if(info != null){
|
list.add(info) ;
|
}
|
}
|
return list ;
|
}
|
/**
|
* 全部Rtu升级信息
|
* @return
|
*/
|
public List<UpgradeRtu> upgradeInfoAll(){
|
if(this.upgradeRtus != null && this.upgradeRtus.size() > 0){
|
return this.upgradeRtus.values().stream().toList();
|
}else{
|
return null ;
|
}
|
}
|
|
///////////////////////////////////////////////////////////
|
//以下方法为内部服务,不对外提供服务
|
///////////////////////////////////////////////////////////
|
|
/**
|
* 判断是否没用任何一个RTU进行过升级,而且超过了时限
|
* @return -1:无一RTU升级且超时,0:无RTU升级但未超时等待,1有RTU升级正常执行
|
*/
|
protected int countNoOneRtuUpgradeInDuration(){
|
if(this.upgradeRtus == null || upgradeRtus.size() == 0){
|
//当前没有任何一个设备进行过升级
|
Long now = System.currentTimeMillis() ;
|
if(now - this.setupDtLong > UpgradeUnit.confVo.noOneRtuUpgradeMaxDuration){
|
return -1 ;
|
}
|
}else{
|
Collection<UpgradeRtu> col = this.upgradeRtus.values() ;
|
for(UpgradeRtu info : col){
|
if(info.currentPackage > 0){
|
//当前有设备进行过升级
|
return 1 ;
|
}
|
}
|
Long now = System.currentTimeMillis() ;
|
if(now - this.setupDtLong > UpgradeUnit.confVo.noOneRtuUpgradeMaxDuration){
|
return -1 ;
|
}
|
}
|
return 0 ;
|
}
|
|
/**
|
* 统计当前正在升级的RTU数量,为同时升级数量限制做准备
|
*/
|
protected void countRunningRtuCount(){
|
int runningTotal = 0 ;
|
Collection<UpgradeRtu> col = this.upgradeRtus.values() ;
|
for(UpgradeRtu info : col){
|
if(info.state == UpgradeRtu.STATE_RUNNING){
|
runningTotal ++ ;
|
}
|
}
|
this.curUgRunningRtuTotal = runningTotal ;
|
}
|
|
/**
|
* 统计需要升级但当前离线RTU的情况,超过时限的设备为升级完成
|
*/
|
protected void countOffRtuAndSetIfOver() {
|
Long now = System.currentTimeMillis() ;
|
if(now - this.setupDtLong > UpgradeUnit.confVo.rtuOffLineWaitDuration){
|
//rtu离线,等待其升级的时长(毫秒),超过配置的最大时长,设置其升级失败,且设置升级任务完成
|
if (this.taskVo.rtuAddrList != null && this.taskVo.rtuAddrList.size() > 0) {
|
Collection<UpgradeRtu> col = this.upgradeRtus.values() ;
|
for(UpgradeRtu info : col){
|
if(info.state == UpgradeRtu.STATE_OFFLINE){
|
info.isOver = true ;
|
info.state = UpgradeRtu.STATE_FAILOFFLINE ;
|
}
|
}
|
}
|
}
|
}
|
|
/**
|
* 统计是否升级全部结束
|
*/
|
protected boolean countIsAllOver() {
|
if (this.taskVo.rtuAddrList != null && this.taskVo.rtuAddrList.size() > 0) {
|
Collection<UpgradeRtu> col = this.upgradeRtus.values() ;
|
for(UpgradeRtu info : col){
|
if(info.isOver == false){
|
return false ;
|
}
|
}
|
}
|
return true ;
|
}
|
}
|