1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
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 ;
    }
}