diff --git a/sec-api/src/main/java/com/imdroid/secapi/client/BeidouClient.java b/sec-api/src/main/java/com/imdroid/secapi/client/BeidouClient.java index 13238a03..bd520bdb 100644 --- a/sec-api/src/main/java/com/imdroid/secapi/client/BeidouClient.java +++ b/sec-api/src/main/java/com/imdroid/secapi/client/BeidouClient.java @@ -41,4 +41,10 @@ public interface BeidouClient { @RequestParam(name = "tenantId") Integer tenantId, @RequestParam(name = "uploadTime") LocalDateTime uploadTime); + @PostMapping("/upgrade_progress") + String onUpgradeProgress(@RequestParam(name = "deviceId") String deviceId, + @RequestParam(name = "msg") String msg); + + @PostMapping("/upgrade_complete") + String onUpgradeComplete(); } diff --git a/sec-api/src/main/java/com/imdroid/secapi/dto/UpgradeState.java b/sec-api/src/main/java/com/imdroid/secapi/dto/UpgradeState.java new file mode 100644 index 00000000..4af1a3f5 --- /dev/null +++ b/sec-api/src/main/java/com/imdroid/secapi/dto/UpgradeState.java @@ -0,0 +1,15 @@ +package com.imdroid.secapi.dto; + +import lombok.Data; + +@Data +public class UpgradeState { + static final public byte STATE_DISCONNECTED = 0; + static final public byte STATE_INFO_ACQUIRING = 1; + static final public byte STATE_UPGRADING = 2; + static final public byte STATE_UPGRADED = 3; + String deviceId; + byte progress=0; + byte state=STATE_DISCONNECTED; //0:未连接;1:获取信息;2:升级中;3:升级完成 + byte code=0; +} diff --git a/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/executor/D331RtcmMessageExecutor.java b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/executor/D331RtcmMessageExecutor.java index 106705d2..9b441206 100644 --- a/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/executor/D331RtcmMessageExecutor.java +++ b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/executor/D331RtcmMessageExecutor.java @@ -66,7 +66,7 @@ public class D331RtcmMessageExecutor implements Executor DeviceChannel deviceChannel = null; for (Device device : deviceList) { if (device.getOpMode() != GnssDevice.OP_MODE_USE) continue; - if (device.getFixedNum()>0 && device.getGnssSampleRate()>1 + if (device.getFixedNum()>100 && device.getGnssSampleRate()>1 && (deviceBs.getD3xxCount()%device.getGnssSampleRate()) != 0) { //if(!UBXUtil.has1005(forwardBytes)) continue; //1005必推 continue; diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/controller/VersionController.java b/sec-beidou/src/main/java/com/imdroid/beidou/controller/VersionController.java index 9a022298..3aa00460 100644 --- a/sec-beidou/src/main/java/com/imdroid/beidou/controller/VersionController.java +++ b/sec-beidou/src/main/java/com/imdroid/beidou/controller/VersionController.java @@ -148,19 +148,27 @@ public class VersionController extends BasicController{ @PostMapping("/sys/ver_mgr/upgrade") @ResponseBody public HttpResult upgradeApp(@RequestParam String firmware, @RequestParam String id_list) throws Exception { - + HttpResp rsp; if(firmware!=null && !firmware.equals("undefined")) { //请求版本服务升级 try { - HttpResp> rsp = versionClient.upgrade(id_list, firmware); + rsp = versionClient.upgrade(id_list, firmware); System.out.println(id_list); } catch (Exception e){ return HttpResult.fail("版本服务未启动"); } + if(rsp.getCode() == HttpResp.HTTP_RSP_OK){ + //设置设备的使用状态为升级,下发连接版本服务器指令 + + return HttpResult.success("开始升级"); + } + else{ + return HttpResult.fail(rsp.getResponseMessage()); + } } - return HttpResult.success("OK"); + return HttpResult.fail("非法固件"); } int getFirmwareList(int page, int limit, List firmwareInfo){ @@ -196,4 +204,11 @@ public class VersionController extends BasicController{ return files.length; } + void startUpgrade(String id_list){ + String[] deviceList = id_list.split(","); + for(String deviceId:deviceList){ + + } + } + } diff --git a/sec-vermgr/src/main/java/com/imdroid/vermgr/service/UpgradeManager.java b/sec-vermgr/src/main/java/com/imdroid/vermgr/service/UpgradeManager.java new file mode 100644 index 00000000..20b7a77a --- /dev/null +++ b/sec-vermgr/src/main/java/com/imdroid/vermgr/service/UpgradeManager.java @@ -0,0 +1,290 @@ +package com.imdroid.vermgr.service; + +import com.imdroid.secapi.client.BeidouClient; +import com.imdroid.secapi.client.HttpResp; +import com.imdroid.vermgr.entity.DeviceApp; +import com.imdroid.vermgr.utils.CRC16; +import io.netty.channel.Channel; +import io.netty.util.AttributeKey; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; + +/****************************************************** + * 批量升级终端版本 + * 单例,要求线程安全 + * 状态机:Idle---收到UpgradeDeviceList--->Upgrading + * ^ | + * | | + * +------Timerout or Complete <-----+ + * + * 流程: + * 1)收到待升级的设备列表UpgradeDeviceList。如果当前处于升级完毕状态,则更新待升级列表。回应答:开始升级/升级未结束 + * 2)TCP通道激活 ---> 获取版本信息 ---> 关联TCP通道和待升级的设备 + * 3)启动升级会话 ---> 实时更新会话 + * 数据结构:deviceId,cur_ver,upgrade_firmware,TCP_channel,upgrade_session + * ****************************************************/ +@Slf4j +@RestController +public class UpgradeManager { + // 版本管理协议 + public static final byte GET_APP_INFO = (byte) 0xA0; + public static final byte APP_INFO_IND = (byte) 0xA1; + public static final byte UPGRADE_IND = (byte) 0xA2; + public static final byte NOT_UPGRADE_IND = (byte) 0xA3; + public static final byte UPGRADE_ACK = (byte) 0xA4; + + @Autowired + static public UpgradeManager INSTANCE; + @Autowired + BeidouClient beidouClient; + + Map upgradeDeviceList = new ConcurrentHashMap<>(); + Map upgradeSessionMap = new ConcurrentHashMap<>(); + boolean isIdleState = true; + TimerTask operationTimerTask = null; + TimerTask txProgressTimerTask = null; + Timer timer; //30s定时器 + + @PostMapping("/upgrade_cmd") + HttpResp upgrade(@RequestParam(name = "deviceList") String deviceList, @RequestParam(name = "verFile") String verFile){ + HttpResp resp = new HttpResp(); + if(onRxUpgradeDeviceList(deviceList.split(","),verFile)){ + resp.setCode(HttpResp.HTTP_RSP_OK); + } + else{ + resp.setCode(HttpResp.HTTP_RSP_FAILED); + resp.setResponseMessage("正在升级中"); + } + return resp; + } + + boolean onRxUpgradeDeviceList(String[] deviceList, String firmware){ + if(isIdleState){ + isIdleState = false; + //清空状态信息和升级会话 + upgradeDeviceList.clear(); + upgradeSessionMap.clear(); + //创建新的状态信息 + for(String deviceId:deviceList){ + Long deviceSn = Long.valueOf(deviceId); + if(deviceSn!=null) { + DeviceApp deviceApp = new DeviceApp(); + deviceApp.setDevice_sn(deviceSn); + deviceApp.setFirmware(firmware); + upgradeDeviceList.put(deviceSn, deviceApp); + } + } + //启动20s定时器,如果这期间没有收到任何数据,则超时失败 + startTimer(); + startTxProgressTimerTask(); + return true; + } + else{ + return false; + } + } + public void onChannelActive(Channel channel){ + // get app info + Long deviceId = (Long) channel.attr(AttributeKey.valueOf("deviceId")).get(); + if(deviceId == null){ + getVerInfo(channel); + } + } + void onChannelInactive(Channel channel){ + // 更新状态为”未连接“ + long sn = (long) channel.attr(AttributeKey.valueOf("sn")).get(); + if(sn > 0){ + DeviceApp deviceApp = upgradeDeviceList.get(sn); + if (deviceApp != null) { + deviceApp.setUpgrade_state(DeviceApp.STATE_DISCONNECTED); + } + upgradeSessionMap.remove(sn); + } + } + void onRxData(Channel channel, byte[] rx_data){ + if(isIdleState){ + // 临时,空闲状态下,如果连续3次收到C,则认为有终端主动请求升级 + if(rx_data[0]==UpgradeSession.CC && rx_data.length<=4){ + //onUpgradeRequest(channel); + } + } + else { + if (rx_data[0] == APP_INFO_IND && rx_data.length > 2) { + onVerInfoReceived(channel, rx_data); + restartTimer(); + } + else { + Long sn = (Long) channel.attr(AttributeKey.valueOf("deviceId")).get(); + if(sn==null) return; + UpgradeSession session = upgradeSessionMap.get(sn); + if (session == null) return; + + boolean session_done = false; + + if (rx_data[0] == UPGRADE_ACK) { + log.info("upgrade ack"); + //outputHex(rx_data); + if (rx_data[5] != 0) { + //终端拒绝升级,原因是产品序列号不对 + session.reject(); + session_done = true; + } + } else { + session_done = session.procYmodem(channel, rx_data); + } + + if (session_done) { + if (isAllUpgraded()) { + //全部发完,等待新版本启动回应答 + stopTimer(); + onUpgradeComplete(); + } + } + } + } + + } + + void onUpgradeComplete(){ + isIdleState = true; + stopTxProgressTimerTask(); + refreshTxProgress(); + beidouClient.onUpgradeComplete(); + } + /*************************************************** + * 定时器处理 + ****************************************************/ + void startTimer(){ + if(operationTimerTask == null) { + operationTimerTask = new TimerTask() { + @Override + public void run() { + onTimerout(); + } + }; + } + timer.schedule(operationTimerTask,15000); + } + void restartTimer(){ + stopTimer(); + startTimer(); + } + void stopTimer(){ + if(operationTimerTask != null){ + operationTimerTask.cancel(); + operationTimerTask = null; + } + } + + void onTimerout(){ + // 超时处理 + for(UpgradeSession session:upgradeSessionMap.values()){ + if(session.getState() == DeviceApp.STATE_UPGRADING) { + session.onTimeout(); + } + } + // + onUpgradeComplete(); + } + + void startTxProgressTimerTask(){ + if(txProgressTimerTask==null){ + txProgressTimerTask = new TimerTask() { + @Override + public void run() { + refreshTxProgress(); + } + }; + } + timer.schedule(txProgressTimerTask,1000,1000); + } + void stopTxProgressTimerTask(){ + if(txProgressTimerTask != null){ + txProgressTimerTask.cancel(); + txProgressTimerTask = null; + log.info("stopTxProgressTimerTask"); + } + } + /*************************************************** + * 获取终端信息 + ****************************************************/ + public void getVerInfo(Channel channel){ + // 向所有连接终端发读取版本请求 + byte get_app_info[] = {GET_APP_INFO,0}; + byte tailer_buff[] = {0,0}; + int crc = CRC16.calculate(get_app_info); + tailer_buff[0] = (byte) (crc>>8); + tailer_buff[1] = (byte) crc; + channel.write(get_app_info); + channel.writeAndFlush(tailer_buff); + } + + void onVerInfoReceived(Channel channel, byte[] rx_data){ + log.info(channel.remoteAddress()+": received app info"); + // 记录获取到的版本信息 + if(rx_data.length<9) return; + int crc = CRC16.calculate(rx_data, 7); + int crc0 = (((short)rx_data[7]&0xFF)<<8) | ((short)rx_data[8]&0xFF); + if(crc != crc0) { + log.info(channel.remoteAddress()+": crc error"); + return; + } + int i=1; + long sn = 0; + for(i=1;i<=4;i++){ + sn = (sn<<8) | ((short)rx_data[i]&0xFF); + } + String cur_ver = ((short)rx_data[i++]&0xFF) + "." + ((short)rx_data[i++]&0xFF); + + // 更新APP记录并检查此终端是否需要升级 + DeviceApp deviceApp = upgradeDeviceList.get(sn); + + if (deviceApp == null) return; + + deviceApp.setVersion(cur_ver); + //记录这个channel是有应答的 + channel.attr(AttributeKey.valueOf("deviceId")).set(sn); + // 如果原来是未连接状态,则改为连接;如果是其他状态则保持不变 + if(deviceApp.getUpgrade_state() == DeviceApp.STATE_DISCONNECTED) { + deviceApp.setUpgrade_state(DeviceApp.STATE_CONNECTED); + } + //启动升级 + UpgradeSession session = new UpgradeSession(deviceApp); + upgradeSessionMap.put(sn, session); + session.start(channel); + } + + /*************************************************** + * 升级 + ****************************************************/ + synchronized public boolean isAllUpgraded(){ + for(DeviceApp deviceApp:upgradeDeviceList.values()){ + if(deviceApp.getUpgrade_state() != DeviceApp.STATE_TX_COMPLETED && + deviceApp.getUpgrade_state() != DeviceApp.STATE_TX_FAILED) { + return false; + } + } + return true; + } + + void refreshTxProgress(){ + for(UpgradeSession session:upgradeSessionMap.values()){ + if(session.getState() == DeviceApp.STATE_UPGRADING) { + beidouClient.onUpgradeProgress(Long.toString(session.getSN()), session.getTxPercentage() + "%"); + } + } + for(DeviceApp deviceApp:upgradeDeviceList.values()){ + if(deviceApp.getUpgrade_state()!=DeviceApp.STATE_UPGRADING){ + beidouClient.onUpgradeProgress(Long.toString(deviceApp.getDevice_sn()),deviceApp.getStrState()); + } + } + } +} diff --git a/sec-vermgr/src/main/java/com/imdroid/vermgr/service/VerManager.java b/sec-vermgr/src/main/java/com/imdroid/vermgr/service/VerManager.java deleted file mode 100644 index 9aa71bd9..00000000 --- a/sec-vermgr/src/main/java/com/imdroid/vermgr/service/VerManager.java +++ /dev/null @@ -1,376 +0,0 @@ -package com.imdroid.vermgr.service; - -import com.imdroid.vermgr.entity.DeviceApp; -import com.imdroid.vermgr.utils.CRC16; -import io.netty.channel.Channel; -import io.netty.util.AttributeKey; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/****************************************************** - * 批量升级终端版本 - * 单例,要求线程安全 - * 流程: - * 1)收到待升级的设备列表UpgradeDeviceList。如果当前处于升级完毕状态,则更新待升级列表。回应答 - * 2)TCP通道激活 ---> 获取版本信息 ---> 关联TCP通道和待升级的设备 - * 3)启动升级会话 ---> 实时更新会话 - * 数据结构:deviceId,cur_ver,upgrade_firmware,TCP_channel,upgrade_session - * ****************************************************/ -@Slf4j -@Service -public class VerManager { - public static final byte GET_APP_INFO = (byte) 0xA0; - public static final byte APP_INFO_IND = (byte) 0xA1; - public static final byte UPGRADE_IND = (byte) 0xA2; - public static final byte NOT_UPGRADE_IND = (byte) 0xA3; - public static final byte UPGRADE_ACK = (byte) 0xA4; - - public static final int NOT_APP_INFO = 0; - public static final int UPGRADING = 1; - public static final int UPGRADE_DONE= 2; - - public static final int STATE_IDLE = 0; - public static final int STATE_VER_GETTING = 1; - public static final int STATE_UPGRADING = 2; - - int mgr_state; - int ack_expected; - int ack_num; - - // thread safety set and map - Map upgradeDeviceList; - Map sessionChannelMap; - - @Data //加这个注解才能用JSON转换 - class WebSocketMsg { - byte state = STATE_IDLE; - byte state_changed = 0; - long sn = 0; - String msg; - } - TimerTask operationTimerTask = null; - TimerTask txProgressTimerTask = null; - - Timer timer; //获取app info操作、升级操作的定时器 - - public static final VerManager INSTANCE = new VerManager(); - - private VerManager(){ - mgr_state = STATE_IDLE; - upgradeDeviceList = new ConcurrentHashMap(); - sessionChannelMap = new ConcurrentHashMap(); - timer = new Timer(); - operationTimerTask = null; - txProgressTimerTask = null; - } - - synchronized public int getState(){ - return mgr_state; - } - synchronized public boolean addAckNum(){ - if(ack_num 0){ - DeviceApp deviceApp = deviceAppMapper.queryById(sn); - if (deviceApp != null) { - deviceApp.setUpgrade_state(state); - deviceAppMapper.setUpgrade(deviceApp); - } - } - } - - /**** 获取终端版本信息 **** - * 这个函数是controller调用,为避免多个用户同时操作,要避免多线程同时调用 - */ - - public void getVerInfo(Channel channel){ - // 向所有连接终端发读取版本请求 - byte get_app_info[] = {GET_APP_INFO,0}; - byte tailer_buff[] = {0,0}; - int crc = CRC16.calculate(get_app_info); - tailer_buff[0] = (byte) (crc>>8); - tailer_buff[1] = (byte) crc; - channel.write(get_app_info); - channel.writeAndFlush(tailer_buff); - } - - void onVerInfoReceived(Channel channel, byte[] rx_data){ - log.info(channel.remoteAddress()+": received app info"); - // 记录获取到的版本信息 - if(rx_data.length<9) return; - int crc = CRC16.calculate(rx_data, 7); - int crc0 = (((short)rx_data[7]&0xFF)<<8) | ((short)rx_data[8]&0xFF); - if(crc != crc0) { - log.info(channel.remoteAddress()+": crc error"); - return; - } - int i=1; - long sn = 0; - for(i=1;i<=4;i++){ - sn = (sn<<8) | ((short)rx_data[i]&0xFF); - } - String cur_ver = ((short)rx_data[i++]&0xFF) + "." + ((short)rx_data[i++]&0xFF); - - // 更新APP记录并检查此终端是否需要升级 - DeviceApp deviceApp = upgradeDeviceList.get(sn); - - if (deviceApp == null) return; - - deviceApp.setVersion(cur_ver); - //记录这个channel是有应答的 - channel.attr(AttributeKey.valueOf("deviceId")).set(sn); - // 如果原来是未连接状态,则改为连接;如果是其他状态则保持不变 - if(deviceApp.getUpgrade_state() == DeviceApp.STATE_DISCONNECTED) { - deviceApp.setUpgrade_state(DeviceApp.STATE_CONNECTED); - } - } - - /**** 批量升级 ****/ - Channel getChannelBySn(long sn){ - for(Channel channel: channelSet){ - long id = (long) channel.attr(AttributeKey.valueOf("sn")).get(); - boolean acked = (boolean) channel.attr(AttributeKey.valueOf("acked")).get(); - if(acked && (id == sn)){ - return channel; - } - } - return null; - } - - synchronized public boolean upgradeBatch(String firmware, ArrayList id_list){ - if(mgr_state != STATE_IDLE) return false; - changeState(STATE_UPGRADING); - - // 清除上次的session - for(UpgradeSession session : sessionChannelMap.values()){ - session.reset(); - } - sessionChannelMap.clear(); - - // 创建升级session - int upgrade_expected = 0; - for(long sn:id_list){ - DeviceApp deviceApp = deviceAppMapper.queryById(sn); - Channel channel = getChannelBySn(sn); - if(deviceApp!=null && channel!=null){ - deviceApp.setFirmware(firmware); - UpgradeSession session = new UpgradeSession(deviceApp); - sessionChannelMap.put(channel,session); - - // 启动升级会话,这里边会改变deviceApp的状态 - if(session.start(channel)) { - upgrade_expected++; - } - deviceAppMapper.setUpgrade(deviceApp); - } - } - - if(upgrade_expected == 0) changeState(STATE_IDLE); - ack_expected = upgrade_expected; - ack_num = 0; - return true; - } - - /**** 消息处理 **** - * 允许多线程操作 - */ - void outputHex(byte[] rx_data) - { - byte[] hex = new byte[rx_data.length*3]; - int pos = 0; - for(int i=0; i>4); - if(hex[pos]<=9) hex[pos]+='0'; - else hex[pos] = (byte) (hex[pos] - 0xA + 'A'); - pos++; - hex[pos] = (byte) (rx_data[i]&0x0F); - if(hex[pos]<=9) hex[pos]+='0'; - else hex[pos] = (byte) (hex[pos] - 0xA + 'A'); - pos++; - hex[pos++] = ' '; - } - log.info(new String(hex)); - } - public void procMsg(Channel channel, byte[] rx_data) { - //outputHex(rx_data); - if(mgr_state == STATE_IDLE){ - // 临时,空闲状态下,如果连续3次收到C,则认为有终端主动请求升级 - if(rx_data[0]==UpgradeSession.CC && rx_data.length<=4){ - //onUpgradeRequest(channel); - } - } - if(mgr_state == STATE_UPGRADING){ - UpgradeSession session = sessionChannelMap.get(channel); - if(session == null) return; - - boolean session_done = false; - - if(rx_data[0]==UPGRADE_ACK){ - log.info("upgrade ack"); - //outputHex(rx_data); - if(rx_data[5]!=0) { - //终端拒绝升级,原因是产品序列号不对 - session.reject(); - session_done = true; - } - } - else { - session_done = session.procYmodem(channel, rx_data); - } - - if(session_done){ - deviceAppMapper.setUpgrade(session.getDeviceApp()); - if(isAllUpgraded()){ - //全部发完,等待新版本启动回应答 - stopTxProgressTimerTask(); - } - onSessionChanged(session); - } - } - - // 查询状态或升级状态下都会收到这个指示 - if(rx_data[0]==APP_INFO_IND && rx_data.length>2) { - onVerInfoReceived(channel, rx_data); - } - } - - -} diff --git a/sec-vermgr/src/main/java/com/imdroid/vermgr/service/VersionHandler.java b/sec-vermgr/src/main/java/com/imdroid/vermgr/service/VersionHandler.java index 9fca637a..147af19e 100644 --- a/sec-vermgr/src/main/java/com/imdroid/vermgr/service/VersionHandler.java +++ b/sec-vermgr/src/main/java/com/imdroid/vermgr/service/VersionHandler.java @@ -13,7 +13,7 @@ public class VersionHandler extends ChannelInboundHandlerAdapter { public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); Channel channel = ctx.channel(); - VerManager.INSTANCE.onChannelActive(channel); + UpgradeManager.INSTANCE.onChannelActive(channel); } @Override @@ -33,14 +33,14 @@ public class VersionHandler extends ChannelInboundHandlerAdapter { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{ Channel channel = ctx.channel(); - VerManager.INSTANCE.procMsg(channel, (byte[]) msg); + UpgradeManager.INSTANCE.onRxData(channel, (byte[]) msg); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); - VerManager.INSTANCE.onChannelInactive(channel); + UpgradeManager.INSTANCE.onChannelInactive(channel); } @Override diff --git a/sec-vermgr/src/main/resources/application.properties b/sec-vermgr/src/main/resources/application.properties new file mode 100644 index 00000000..ec17821b --- /dev/null +++ b/sec-vermgr/src/main/resources/application.properties @@ -0,0 +1,10 @@ +server.port=9914 +server.servlet.context-path=/ + +spring.application.name=vermgr +spring.application.build=20250519 + +version_server_port = 9916 + +#version manage +version.path = firmware \ No newline at end of file