package com.hdl.photovoltaic.other; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.text.TextUtils; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import com.hdl.linkpm.sdk.core.exception.HDLException; import com.hdl.linkpm.sdk.device.bean.DeviceOidInfoBean; import com.hdl.linkpm.sdk.ota.bean.CloudDeviceFirmwaresBean; import com.hdl.linkpm.sdk.ota.bean.CloudGatewayDriversBean; import com.hdl.linkpm.sdk.ota.bean.DeviceFirmwareBean; import com.hdl.linkpm.sdk.ota.bean.DownloadUrlBean; import com.hdl.linkpm.sdk.ota.bean.GatewayDriverBean; import com.hdl.photovoltaic.HDLApp; import com.hdl.photovoltaic.config.UserConfigManage; import com.hdl.photovoltaic.internet.HttpClient; import com.hdl.photovoltaic.internet.HttpServer.MyNanoHttpServer; import com.hdl.photovoltaic.internet.HttpServer.MyNanoHttpService; import com.hdl.photovoltaic.internet.TcpClient; import com.hdl.photovoltaic.internet.api.HttpApi; import com.hdl.photovoltaic.internet.api.TopicApi; import com.hdl.photovoltaic.listener.CloudCallBeak; import com.hdl.photovoltaic.listener.LinkCallBack; import com.hdl.photovoltaic.utils.AesUtils; import com.hdl.photovoltaic.utils.AppManagerUtils; import com.hdl.photovoltaic.utils.Md5Utils; import com.hdl.sdk.link.common.exception.HDLLinkException; import com.hdl.sdk.link.common.utils.ByteUtils; import com.hdl.sdk.link.core.bean.eventbus.BaseEventBus; import com.hdl.sdk.link.core.callback.HDLLinkCallBack; import org.apache.commons.io.FileUtils; import org.greenrobot.eventbus.EventBus; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Objects; import okhttp3.ResponseBody; /** * ota升级逻辑 */ public class HdlOtaLogic { private static volatile HdlOtaLogic sHdlOtaLogic; /** * 获取当前对象 * * @return HdlDeviceLogic */ public static synchronized HdlOtaLogic getInstance() { if (sHdlOtaLogic == null) { synchronized (HdlOtaLogic.class) { if (sHdlOtaLogic == null) { sHdlOtaLogic = new HdlOtaLogic(); } } } return sHdlOtaLogic; } /** * Intent专门启动本地服务 * * @return Intent */ public Intent getIntent() { return intentServer; } public void setIntent(Intent intent) { this.intentServer = intent; } private Intent intentServer = null; private ServiceConnection mServiceConnection = null; /** * 服务是否开启 true=开启 */ public boolean mServiceStart = false; /** * true表示终止下载升级文件(用户行为) */ private boolean stopDownload = false; //region -----固件升级--------- /** * 向云端获取逆变器oid列表 * 前提条件:要上传逆变器oid列表给云端 * * @param callBack - */ public void getCloudOidList(CloudCallBeak> callBack) { String requestUrl = HttpApi.POST_DEVICE_OID_LIST_GET; JsonObject json = new JsonObject(); json.addProperty("homeId", UserConfigManage.getInstance().getHomeId()); HttpClient.getInstance().requestHttp(requestUrl, json.toString(), new CloudCallBeak() { @Override public void onSuccess(String json) { if (TextUtils.isEmpty(json)) { if (callBack != null) { callBack.onSuccess(new ArrayList<>()); } return; } Gson gson = new Gson(); Type typeOfT = new TypeToken>() { }.getType(); List list = gson.fromJson(json, typeOfT); if (callBack != null) { callBack.onSuccess(list); } } @Override public void onFailure(HDLException e) { if (callBack != null) { callBack.onFailure(e); } } }); } /** * 向云端获取【当前设备固件】列表 * 前提条件:设备自动上报oid信息给云端 * * @param deviceOidId oid云端id * @param callBack - */ public void getCurrentDeviceFirmwares(String deviceOidId, CloudCallBeak> callBack) { String requestUrl = HttpApi.POST_OTA_GET_DeviceFirmwares; JsonObject json = new JsonObject(); json.addProperty("deviceOidId", deviceOidId); json.addProperty("homeId", UserConfigManage.getInstance().getHomeId()); HttpClient.getInstance().requestHttp(requestUrl, json.toString(), new CloudCallBeak() { @Override public void onSuccess(String json) { if (TextUtils.isEmpty(json)) { if (callBack != null) { callBack.onSuccess(new ArrayList<>()); } return; } Gson gson = new Gson(); Type typeOfT = new TypeToken>() { }.getType(); List list = gson.fromJson(json, typeOfT); if (callBack != null) { callBack.onSuccess(list); } } @Override public void onFailure(HDLException e) { if (callBack != null) { callBack.onFailure(e); } } }); } /** * 向云端获取【设备新固件】列表 * 前提条件:要通过平台软件上传新固件 * * @param hardwareModel 硬件型号 * @param osImageId 系统镜像id * @param callBack - */ public void getNewDeviceFirmwares(String hardwareModel, String osImageId, CloudCallBeak> callBack) { String requestUrl = HttpApi.POST_OTA_GET_CloudDeviceFirmwares; JsonObject json = new JsonObject(); json.addProperty("hardwareModel", hardwareModel); json.addProperty("osImageId", osImageId); // json.addProperty("protocolType", "ZIGBEE");//协议类型,可用值:BUSPRO,KNX,ZIGBEE,OTHER HttpClient.getInstance().requestHttp(requestUrl, json.toString(), new CloudCallBeak() { @Override public void onSuccess(String json) { if (TextUtils.isEmpty(json)) { if (callBack != null) { callBack.onSuccess(new ArrayList<>()); } return; } Gson gson = new Gson(); Type typeOfT = new TypeToken>() { }.getType(); List list = gson.fromJson(json, typeOfT); if (callBack != null) { callBack.onSuccess(list); } } @Override public void onFailure(HDLException e) { if (callBack != null) { callBack.onFailure(e); } } }); } /** * 向云端发起【设备固件】升级OTA指令 * * @param deviceOidId 设备id * @param firmwareVersionId 固件版本id * @param callBack- */ public void upgradeDeviceFirmware(String deviceOidId, String firmwareVersionId, CloudCallBeak callBack) { String requestUrl = HttpApi.POST_OTA_DeviceFirmwareUpgrade; JsonObject json = new JsonObject(); json.addProperty("homeId", UserConfigManage.getInstance().getHomeId()); json.addProperty("deviceOidId", deviceOidId); json.addProperty("firmwareVersionId", firmwareVersionId); HttpClient.getInstance().requestHttp(requestUrl, json.toString(), new CloudCallBeak() { @Override public void onSuccess(String json) { if (callBack != null) { callBack.onSuccess(true); } } @Override public void onFailure(HDLException e) { if (callBack != null) { callBack.onFailure(e); } } }); } //endregion //region -----驱动升级--------- /** * 向云端获取【当前设备驱动】列表 * 前提条件:设备自动上报oid信息给云端 * * @param oid 网关设备oid * @param callBack - */ public void getCurrentGatewayDrivers(String oid, CloudCallBeak> callBack) { String requestUrl = HttpApi.POST_OTA_GET_GatewayDrivers; JsonObject json = new JsonObject(); json.addProperty("homeId", UserConfigManage.getInstance().getHomeId()); json.addProperty("oid", oid); HttpClient.getInstance().requestHttp(requestUrl, json.toString(), new CloudCallBeak() { @Override public void onSuccess(String json) { if (TextUtils.isEmpty(json)) { if (callBack != null) { callBack.onSuccess(new ArrayList<>()); } return; } Gson gson = new Gson(); Type typeOfT = new TypeToken>() { }.getType(); List list = gson.fromJson(json, typeOfT); if (callBack != null) { callBack.onSuccess(list); } } @Override public void onFailure(HDLException e) { if (callBack != null) { callBack.onFailure(e); } } }); } /** * 向云端获取【设备新驱动】列表 * 前提条件:要通过平台软件上传新驱动 * * @param driveCode 驱动编号或驱动名称 * @param osImageId 驱动类型id * @param callBack - */ public void getNewGatewayDrivers(String driveCode, String osImageId, CloudCallBeak callBack) { String requestUrl = HttpApi.POST_OTA_GET_CloudGatewayDrivers; JsonObject json = new JsonObject(); json.addProperty("driveCode", driveCode); json.addProperty("osImageId", osImageId); HttpClient.getInstance().requestHttp(requestUrl, json.toString(), new CloudCallBeak() { @Override public void onSuccess(String json) { if (TextUtils.isEmpty(json)) { if (callBack != null) { callBack.onSuccess(new CloudGatewayDriversBean()); } return; } Gson gson = new Gson(); Type typeOfT = new TypeToken() { }.getType(); CloudGatewayDriversBean cloudGatewayDriversBean = gson.fromJson(json, typeOfT); if (callBack != null) { callBack.onSuccess(cloudGatewayDriversBean); } } @Override public void onFailure(HDLException e) { if (callBack != null) { callBack.onFailure(e); } } }); } /** * 云端发起【设备驱动升级】OTA指令 * * @param oid 网关设备oid * @param driverVersionId 驱动版本id * @param callBack - */ public void upgradeGatewayDriver(String oid, String driverVersionId, CloudCallBeak callBack) { String requestUrl = HttpApi.POST_OTA_GatewayDriverUpgrade; JsonObject json = new JsonObject(); json.addProperty("homeId", UserConfigManage.getInstance().getHomeId()); json.addProperty("oid", oid); json.addProperty("driverVersionId", driverVersionId); HttpClient.getInstance().requestHttp(requestUrl, json.toString(), new CloudCallBeak() { @Override public void onSuccess(String json) { if (callBack != null) { callBack.onSuccess(true); } } @Override public void onFailure(HDLException e) { if (callBack != null) { callBack.onFailure(e); } } }); } //endregion /** * 设置终止下载文件指令 */ public void setStopDriversDownload() { this.stopDownload = true; } /** * 向云端获取【设备升级文件(驱动,固件)下载】升级文件 * * @param downloadUrl 云端驱动地址 */ public void getDeviceUpgradeDownloadFile(String downloadUrl, CloudCallBeak callBack) { HttpClient.getInstance().downLoadFile(downloadUrl, new CloudCallBeak() { @Override public void onSuccess(ResponseBody s) { if (callBack != null) { callBack.onSuccess(s); } } @Override public void onFailure(HDLException e) { if (callBack != null) { callBack.onFailure(e); } } }); } /** * 本地升级包推送(告诉网关下载升级包地址等信息) * * @param mac 物理设备mac * @param oid 物理设备oid * @param module 模块信息 * @param version 版本号 * @param size 升级包大小 * @param url 升级包下载地址(存储在本地) * @param sign_method md5加密字符串 * @param linkCallBack - */ public void pushUpgradePacketInfo(String mac, String oid, String module, String version, String size, String url, String sign_method, LinkCallBack linkCallBack) { String requestUrl = TopicApi.SET_GATEWAY_UPGRADE_INFO; JsonObject json = new JsonObject(); json.addProperty("oid", oid);// 物理设备oid json.addProperty("module", module);//模块信息 json.addProperty("version", version);//版本号 // json.addProperty("is_diff", is_diff);//true=增量包,false=全量包 json.addProperty("size", size);//升级包大小 json.addProperty("url", url);//升级包下载地址 json.addProperty("sign_method", sign_method);//升级包签名类型:md5 TcpClient.getInstance().sendDataToLinkGateway(mac, requestUrl, json, "", new HDLLinkCallBack() { @Override public void onSuccess(String msg) { if (linkCallBack != null) { linkCallBack.onSuccess(msg); } } @Override public void onError(HDLLinkException e) { if (linkCallBack != null) { linkCallBack.onError(e); } } }); } /** * 启动本地服务 */ public void startLocalService(ServiceConnection serviceConnection) { //本地 //1,建立本地服务; //2,告诉网关手机ip和端口; //实例化 获取ip 地址 MyNanoHttpServer.getInstance(AppManagerUtils.getAppManager().getIPAddress(HDLApp.getInstance())); //MyNanoHttpServer.getInstance("127.0.0.1"); intentServer = new Intent(); intentServer.setClass(HDLApp.getInstance(), MyNanoHttpService.class); this.mServiceConnection = serviceConnection; this.mServiceStart = true; //启动服务监听 HDLApp.getInstance().bindService(intentServer, serviceConnection, Context.BIND_AUTO_CREATE); } /** * 关闭本地服务 */ public void unLocalService() { if (intentServer != null && this.mServiceConnection != null) { HDLApp.getInstance().unbindService(this.mServiceConnection); this.mServiceStart = false; } } /** * 【测试】处理下载文件数据 * * @param zipData 文件数据流对象 * @param md5 检验文件MD5值 * @return 返回解密的byte[] */ public boolean testDisposeDownLoadFile(String fullPath, ResponseBody zipData, String md5) { String driver = driver_type; //-100:没有数据;-101:没有md5秘钥;-102:md5秘钥不对;-103:有异常; if (zipData == null) { this.eventBusPost(fullPath, driver, -100, "没有数据"); return false; } if (TextUtils.isEmpty(md5)) { this.eventBusPost(fullPath, driver, -101, "没有md5秘钥"); return false; } InputStream is = null; byte[] buf = new byte[1204 * 4]; int len = 0; FileOutputStream fos = null; File file = null; try { file = new File(fullPath); is = zipData.byteStream(); long total = zipData.contentLength(); if (total == 0) { this.eventBusPost(fullPath, driver, -100, "没有数据"); return false; } fos = new FileOutputStream(file); long sum = 0; while ((len = is.read(buf)) != -1) { fos.write(buf, 0, len); sum += len; //todo 下载中,可以自己计算的进度条(可以广播出去) int progress = (int) (100 * sum * 1.0f / total); if (sum != total) { //不等于100都要报,等于100处理整块逻辑完之后再报100; this.eventBusPost(fullPath, driver, progress, "正常上报"); } } fos.flush(); fos.close(); is.close(); byte[] bytes = FileUtils.readFileToByteArray(file); String fileMD5 = Md5Utils.encodeMD5(bytes); if (TextUtils.isEmpty(fileMD5)) { this.eventBusPost(fullPath, driver, -106, "文件生成md5失败."); return false; } if (!md5.equals(fileMD5)) { this.eventBusPost(fullPath, driver, -107, "md5比对失败."); return false; } this.eventBusPost(fullPath, driver, 100, "下载完成."); return true; } catch (Exception e) { this.eventBusPost(fullPath, driver, -103, e.getMessage()); e.printStackTrace(); } return false; } /** * 处理下载文件数据 * * @param fileFullPath 文件全路径 * @param zipData 文件数据流对象 * @param md5 检验文件MD5值 * @param driver 驱动或者固件 * @return 成功true */ public boolean disposeDownLoadFile(String fileFullPath, ResponseBody zipData, String md5, String driver) { this.stopDownload = false; //-100:没有数据;-101:没有md5秘钥;-102:md5秘钥不对;-103:有异常; if (zipData == null) { this.eventBusPost(fileFullPath, driver, -100, "没有数据"); return false; } if (TextUtils.isEmpty(md5)) { this.eventBusPost(fileFullPath, driver, -101, "没有md5秘钥"); return false; } InputStream is = null; byte[] buf = new byte[1204 * 4]; int len = 0; FileOutputStream fos = null; File file = null; try { file = new File(fileFullPath); is = zipData.byteStream(); long total = zipData.contentLength(); if (total == 0) { this.eventBusPost(fileFullPath, driver, -100, "没有数据"); return false; } fos = new FileOutputStream(file); long sum = 0; while ((len = is.read(buf)) != -1) { if (this.stopDownload) { this.eventBusPost(fileFullPath, driver, -108, "用户取消下载."); fos.flush(); fos.close(); is.close(); //todo 用户终止继续读取数据; return false; } fos.write(buf, 0, len); sum += len; //todo 下载中,可以自己计算的进度条(可以广播出去) int progress = (int) (100 * sum * 1.0f / total); if (sum != total) { //不等于100都要报,等于100处理整块逻辑完之后再报100; this.eventBusPost(fileFullPath, driver, progress, "正常上报"); } } fos.flush(); fos.close(); is.close(); byte[] bytes = FileUtils.readFileToByteArray(file); //下载文件需要解密之后再进行md5去做比对 byte[] decrypt = AesUtils.decrypt(bytes); if (decrypt == null) { this.eventBusPost(fileFullPath, driver, -105, "aes解密失败."); return false; } String fileMD5 = Md5Utils.encodeMD5(decrypt); if (TextUtils.isEmpty(fileMD5)) { this.eventBusPost(fileFullPath, driver, -106, "文件生成md5失败."); return false; } if (!md5.equals(fileMD5)) { this.eventBusPost(fileFullPath, driver, -107, "md5比对失败."); return false; } //todo 注意:解密之后,要重新写数据; FileUtils.writeByteArrayToFile(file, decrypt, false); this.eventBusPost(fileFullPath, driver, 100, "下载完成."); return true; } catch (Exception e) { this.eventBusPost(fileFullPath, driver, -103, e.getMessage()); e.printStackTrace(); } return false; } /** * 发布进度主题 * * @param type 驱动:driver,固件:firmware * @param progressValue 进度值 * @param describe 描述文本 */ public void eventBusPost(String fileFullPath, String type, int progressValue, String describe) { HdlLogLogic.print("进度值====" + progressValue + ""); if (this.stopDownload) { HdlFileLogic.getInstance().deleteFile(fileFullPath);//删除下载不完整数据; BaseEventBus baseEventBus = new BaseEventBus(); baseEventBus.setTopic(localDownloadProgress); Progress progress = new Progress(); progress.step = -108; progress.describe = "用户取消下载."; baseEventBus.setType(type); baseEventBus.setData(progress); EventBus.getDefault().post(baseEventBus); return; } // HdlThreadLogic.runMainThread(new Runnable() { // @Override // public void run() { BaseEventBus baseEventBus = new BaseEventBus(); baseEventBus.setTopic(localDownloadProgress); Progress progress = new Progress(); progress.step = progressValue; progress.describe = describe; progress.type = type; baseEventBus.setType(type); baseEventBus.setData(progress); EventBus.getDefault().post(baseEventBus); // } // }); } //自定义上报下载驱动或固件进度主题 public static final String localDownloadProgress = "cloud/download/progress/up"; public static final String driver_type = "driver"; public static final String firmware_type = "firmware"; public static class Progress { //进度值 public int step; //进度描述信息 public String describe; //表示是驱动,固件; public String type; } }