| | |
| | | |
| | | import com.alibaba.fastjson2.annotation.JSONField; |
| | | import com.dy.common.softUpgrade.parse.HexFileParse; |
| | | import com.dy.common.softUpgrade.parse.HexFileVo; |
| | | 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; |
| | |
| | | @Data |
| | | public class UpgradeTask { |
| | | |
| | | 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 String softFileName ;//升级软件(hex)文件名称 |
| | | public String softStoreAddr ;//升级程序存放地址(4字节,8字符HEX字符串),升级程序在FLASH中存放地址 |
| | | public String softStartAddr ;//程序覆盖起始地址(4字节,8字符HEX字符串),被刷新程序的起始地址高字节在前 ,低字节在后 |
| | | public UpgradeTaskVo taskVo ;//升级任务值对象 |
| | | |
| | | @JSONField(serialize = false) |
| | | public byte[][] softData ;//升级程序数据(每包数据是512字节) |
| | | @JSONField(serialize = false) |
| | | public int softByteSrc16;//升级程序校验码 CRC16 |
| | | |
| | | public int softBytesCalculate;//升级程序字节数(按公式计算) |
| | | |
| | | public List<String> rtuAddrList ;//需要升级的RTU地址集合 |
| | | protected byte[][] softFileDataGrp ;//以512字节为单位把升级程序数据分组 |
| | | |
| | | @JSONField(serialize = false) |
| | | public Map<String, UpgradeRtu> upgradeState ;//升级状态 |
| | | protected Map<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 ; |
| | | } |
| | | /** |
| | | * 初始化配置信息 |
| | |
| | | } |
| | | /** |
| | | * 设置升级任务 |
| | | * @param softFileName 升级程序文件名 |
| | | * @param softStoreAddr 升级程序存放地址 |
| | | * @param softStartAddr 程序覆盖起始地址 |
| | | * @param softFileData 升级程序字节数组 |
| | | * @param softBytesCalculate 升级程序字节数(按公式计算) |
| | | * @param rtuAddrList 升级RTU |
| | | * @param taskVo UpgradeTaskVo 升级任务对象 |
| | | * @throws Exception |
| | | */ |
| | | public void setTask(String softFileName, |
| | | String softStoreAddr, |
| | | String softStartAddr, |
| | | byte[] softFileData, |
| | | Integer softBytesCalculate, |
| | | List<String> rtuAddrList) throws Exception { |
| | | if(softFileName == null || softFileName.trim().length() == 0){ |
| | | 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(softStoreAddr == null || softStoreAddr.trim().length() != 8){ |
| | | if(taskVo.softStoreAddr == null || taskVo.softStoreAddr.trim().length() != 8){ |
| | | throw new Exception("升级程序存放地址不合法,必须是8字符(十六进制)的字符串") ; |
| | | } |
| | | if(softStartAddr == null || softStartAddr.trim().length() != 8){ |
| | | if(taskVo.softStartAddr == null || taskVo.softStartAddr.trim().length() != 8){ |
| | | throw new Exception("程序覆盖起始地址不合法,必须是8字符(十六进制)的字符串") ; |
| | | } |
| | | if(softFileData == null || softFileData.length <= 0){ |
| | | if(taskVo.softFileData == null || taskVo.softFileData.length <= 0){ |
| | | throw new Exception("升级程序内容必须提供") ; |
| | | } |
| | | if(rtuAddrList == null || rtuAddrList.size() <= 0){ |
| | | 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.softFileName = softFileName; |
| | | this.softStoreAddr = softStoreAddr; |
| | | this.softStartAddr = softStartAddr; |
| | | this.softBytesCalculate = softBytesCalculate; |
| | | this.rtuAddrList = rtuAddrList; |
| | | this.setupDtLong = System.currentTimeMillis() ; |
| | | this.taskVo = taskVo ; |
| | | |
| | | this.upgradeState = new HashMap<>(); |
| | | if(softFileData != null && softFileData.length >0){ |
| | | HexFileVo vo = new HexFileParse().doParse(softFileData); |
| | | this.softData = vo.listByte512.toArray(new byte[0][]); |
| | | this.softByteSrc16 = vo.bytesCrc16 ; |
| | | this.upgradeRtus = new HashMap<>(); |
| | | if(taskVo.softFileData != null && taskVo.softFileData.length >0){ |
| | | List<byte[]> listBytes = new HexFileParse().splitBytesByUnit512(taskVo.softFileData); |
| | | this.softFileDataGrp = listBytes.toArray(new byte[0][]); |
| | | for(String rtuAddr : this.taskVo.rtuAddrList){ |
| | | //此时状态设置成离线状态 |
| | | UpgradeRtuDev ugRtu = new UpgradeRtuDev(this, rtuAddr, this.taskVo.softFileData.length, UpgradeRtuDev.STATE_OFFLINE) ; |
| | | this.upgradeRtus.put(rtuAddr, ugRtu) ; |
| | | } |
| | | } |
| | | } |
| | | /** |
| | | * RTU有上行数据了,触发下发升级数据 |
| | | * @param rtuAddr |
| | | * @param code |
| | | * @param callback |
| | | * @param callbackCom |
| | | */ |
| | | public void trigger(String rtuAddr, String code, String protocolName, Short protocolVersion, Callback callback){ |
| | | if(upgradeState != null && upgradeState.size() > 0 |
| | | && rtuAddrList != null && rtuAddrList.size() > 0){ |
| | | UpgradeRtu info = upgradeState.get(rtuAddr) ; |
| | | if(info == null){ |
| | | if(rtuAddrList.contains(rtuAddr)){ |
| | | info = new UpgradeRtu(this, rtuAddr, softData.length) ; |
| | | upgradeState.put(rtuAddr, info) ; |
| | | 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, rtuAddr, this.taskVo.softFileData.length) ; |
| | | upgradeRtus.put(rtuAddr, ugRtu) ; |
| | | }else{ |
| | | //rtu不在升级之列 |
| | | return ; |
| | | } |
| | | } |
| | | if(info != null){ |
| | | info.trigger(code, protocolName, protocolVersion, this.softData, callback) ; |
| | | 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(){ |
| | | this.rtuAddrList.clear(); |
| | | this.upgradeState.clear(); |
| | | } |
| | | |
| | | /** |
| | | * 升级任务是否完成 |
| | | * @return |
| | | */ |
| | | public boolean isOver() { |
| | | boolean isOver = true ; |
| | | if(upgradeState != null && upgradeState.size() > 0){ |
| | | Collection<UpgradeRtu> col = upgradeState.values() ; |
| | | for(UpgradeRtu info : col){ |
| | | if(info.state == UpgradeRtu.STATE_UNSTART){ |
| | | isOver = false ; |
| | | break ; |
| | | }else if(info.state == UpgradeRtu.STATE_RUNNING){ |
| | | isOver = false ; |
| | | break ; |
| | | } |
| | | } |
| | | } |
| | | return isOver ; |
| | | 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(); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public UpgradeState currentUpgradeState() { |
| | | UpgradeState state = new UpgradeState() ; |
| | | if(rtuAddrList != null && rtuAddrList.size() > 0){ |
| | | state.rtuTotal = rtuAddrList.size() ; |
| | | if(upgradeState != null && upgradeState.size() > 0){ |
| | | Collection<UpgradeRtu> col = upgradeState.values() ; |
| | | if(this.taskVo.rtuAddrList != null && this.taskVo.rtuAddrList.size() > 0){ |
| | | state.rtuTotal = this.taskVo.rtuAddrList.size() ; |
| | | if(this.upgradeRtus != null && this.upgradeRtus.size() > 0){ |
| | | Collection<UpgradeRtu> col = this.upgradeRtus.values() ; |
| | | for(UpgradeRtu info : col){ |
| | | if(info.state == UpgradeRtu.STATE_UNSTART){ |
| | | if(info.state == UpgradeRtuDev.STATE_OFFLINE){ |
| | | state.offLineTotal ++ ; |
| | | }else if(info.state == UpgradeRtuDev.STATE_UNSTART){ |
| | | state.unStartTotal ++ ; |
| | | }else if(info.state == UpgradeRtu.STATE_RUNNING){ |
| | | }else if(info.state == UpgradeRtuDev.STATE_RUNNING){ |
| | | state.runningTotal ++ ; |
| | | }else if(info.state == UpgradeRtu.STATE_SUCCESS) { |
| | | }else if(info.state == UpgradeRtuDev.STATE_SUCCESS) { |
| | | state.successTotal++; |
| | | state.overTotal++; |
| | | }else if(info.state == UpgradeRtu.STATE_FAILONE) { |
| | | }else if(info.state == UpgradeRtuDev.STATE_FAILONE) { |
| | | state.failOneTotal++; |
| | | state.failTotal++; |
| | | state.overTotal++; |
| | | }else if(info.state == UpgradeRtu.STATE_FAIL) { |
| | | }else if(info.state == UpgradeRtuDev.STATE_FAIL) { |
| | | state.failTotal++; |
| | | } |
| | | if(info.isOver){ |
| | | state.overTotal++; |
| | | } |
| | | } |
| | |
| | | * @return |
| | | */ |
| | | public UpgradeRtu upgradeInfos(String rtuAddr){ |
| | | return upgradeState.get(rtuAddr) ; |
| | | return this.upgradeRtus.get(rtuAddr) ; |
| | | } |
| | | |
| | | /** |
| | | * Rtu升级信息 |
| | | * 一些Rtu升级信息 |
| | | * @param rtuAddrList |
| | | * @return |
| | | */ |
| | | public List<UpgradeRtu> upgradeInfos(List<String> rtuAddrList){ |
| | | List<UpgradeRtu> list = new ArrayList<>() ; |
| | | for(String rtuAddr : rtuAddrList){ |
| | | UpgradeRtu info = upgradeState.get(rtuAddr) ; |
| | | 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数量,为受限同时升级数量做准备 |
| | | */ |
| | | protected void statisticsRunningRtuCount(){ |
| | | 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 statisticsOffRtuCountAndSet() { |
| | | 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 statisticsIsAllOver() { |
| | | 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 ; |
| | | } |
| | | } |