using DotNetCore.CAP; using Furion; using Furion.DependencyInjection; using Furion.DistributedIDGenerator; using Microsoft.Extensions.Caching.Distributed; using Nirvana.Common; using SqlSugar; using System; using System.Linq; using System.Text; using System.Threading.Tasks; using Waste.Application.SubscribeInfo; using Waste.Domain; namespace Waste.Application.ThirdApiInfo { /// /// 设备对接接口 /// public class OpenService : IOpenService, ITransient { private static string ApiUrl = App.Configuration["SZDevPlatSetting:ApiUrl"]; private static string UserId = App.Configuration["SZDevPlatSetting:UserId"]; private static string ApiSecret = App.Configuration["SZDevPlatSetting:ApiSecret"]; private static string ApiSecretHash = App.Configuration["SZDevPlatSetting:ApiSecretHash"]; private static string WebSocketUrl = App.Configuration["SZDevPlatSetting:SocketUrl"]; private readonly ISqlSugarRepository repository; private readonly SqlSugarClient dbClient; private readonly ISuZhouService _suZhouService; private readonly ILoggerService _loggerService; private readonly ICapPublisher _capBus; private readonly IDistributedCache _cahce; private readonly IResultService _resultService; private readonly IDistributedIDGenerator _idgen; public OpenService(ISqlSugarRepository sqlSugarRepository, ISuZhouService suZhouService, ILoggerService loggerService, ICapPublisher capPublisher, IDistributedCache distributedCache, IResultService resultService, IDistributedIDGenerator distributedIDGenerator) { repository = sqlSugarRepository; dbClient = repository.Context; _suZhouService = suZhouService; _loggerService = loggerService; _capBus = capPublisher; _cahce = distributedCache; _resultService = resultService; _idgen = distributedIDGenerator; } /// /// 更新上报状态 /// /// /// public async Task UpdateStatusAsync(UpdateStatusDto data) { //await _capBus.PublishAsync("result.service.update", data); //return new ResultInfo(ResultState.SUCCESS, "success"); Guid resultid = Guid.Empty; if (!string.IsNullOrEmpty(data.ResultId) && Guid.TryParse(data.ResultId, out resultid)) { if (await dbClient.Queryable().AnyAsync(x => x.ResultId == resultid)) { await dbClient.Updateable().SetColumns(x => new W_ResultExt { Status = data.status }).Where(x => x.ResultId == resultid).ExecuteCommandAsync(); } else { var insertdata = new W_ResultExt { Id = _idgen.Create().ToGuid(), Status = data.status, CreateTime = DateTime.Now, ResultId = resultid }; await dbClient.Insertable(insertdata).ExecuteCommandAsync(); } return new ResultInfo(ResultState.SUCCESS, "success"); } return new ResultInfo(ResultState.SUCCESS, "记录id未找到"); } /// /// 获取设备上报相关信息 /// /// /// public async Task GetDevInfoAsync(GetDevInfoRequestDto data) { //更新上报记录结果 Guid resultid = Guid.Empty; //这里进行去重处理 if (!string.IsNullOrEmpty(data.ResultId) && Guid.TryParse(data.ResultId, out resultid)) { var device = await dbClient.Queryable().FirstAsync(x => x.Ecode == data.ECode); if (device == null) { return new ResultInfo(ResultState.FAIL, "设备未找到"); } var devicesecret = await dbClient.Queryable().FirstAsync(x => x.DeviceId == device.Id); if (devicesecret == null || string.IsNullOrEmpty(devicesecret.Secret) || string.IsNullOrEmpty(devicesecret.SecretHash) || string.IsNullOrEmpty(devicesecret.DevId)) { return new ResultInfo(ResultState.FAIL, "设备还未获取验证信息"); } int timestamp = _suZhouService.GetUTCTimestamp(); int noncestr = _suZhouService.GetNonce(); var result = await dbClient.Queryable().FirstAsync(x => x.Id == resultid); if (result == null) { return new ResultInfo(ResultState.SUCCESS, "记录id未找到"); } var returndata = new GetDevInfoResponseDto { DeviceId = devicesecret.DevId, noncestr = noncestr, timestamp = timestamp, Secret = devicesecret.Secret, SecretHash = devicesecret.SecretHash, UserId = UserId, PostUrl = ApiUrl, ScanningTime = timestamp, ResultId = resultid, trash = result.Registration, Weight = result.GrossWeight.ToDouble(), status = 0, IsSuccessed = true, type = TrashType(result.WasteType) }; string[] paramlist = new string[] { returndata.Weight.ToString(),returndata.trash,returndata.type.ToString(),returndata.ScanningTime.ToString(),returndata.status.ToString()}; returndata.sign = _suZhouService.GetUserApiSign(returndata.Secret, paramlist); return new ResultInfo(ResultState.SUCCESS, "success", returndata); } else { var device = await dbClient.Queryable().FirstAsync(x => x.Ecode == data.ECode); if (device == null) { return new ResultInfo(ResultState.FAIL, "设备未找到"); } if (device.Status == (int)DeviceStatus.Error) { return new ResultInfo(ResultState.FAIL, "设备已停止运行"); } var returndata = new GetDevInfoResponseDto { ResultId = _idgen.Create().ToGuid(), UserId = UserId, PostUrl = ApiUrl }; var devicesecret = await dbClient.Queryable().FirstAsync(x => x.DeviceId == device.Id); if (devicesecret == null || string.IsNullOrEmpty(devicesecret.Secret) || string.IsNullOrEmpty(devicesecret.SecretHash) || string.IsNullOrEmpty(devicesecret.DevId)) { return new ResultInfo(ResultState.FAIL, "设备还未获取验证信息"); } //解析协议,IC卡数据@垃圾桶编号@厨余垃圾@7.91 // 00000000003031 40 0F00010009 40 C6E4CBFBC0ACBBF8 40 31352E39 // 00000000003031 40 000F000002 40 C6E4CBFBC0ACBBF8 40 35312E30 0D0A if (!string.IsNullOrEmpty(data.data) && data.data.Length > 52) { data.data = data.data.Replace(" ", ""); //收到的为16进制,对数据进行解析,0-4预留,5-垃圾种类,6-垃圾桶大小,7-@,8-12垃圾桶编号,13@,14-21垃圾种类汉字,22@,23-结束重量, OD OA 回车换行 data.data = data.data.Substring(0, data.data.Length - 4); var trashhex = data.data.Substring(16, 10); //垃圾桶编号 var typehex = data.data.Substring(28, 16); //垃圾种类 var sizehex = data.data.Substring(12, 2);//桶大小,30-小桶,31-大桶 var weighthex = data.data.Substring(46, data.data.Length - 46); returndata.trash = HextToDec(trashhex).ToString(); //垃圾桶编号使用10进制 var type = GetChsFromHex(typehex); var weight = GetChsFromHex(weighthex); returndata.type = TrashType(type); returndata.Weight = weight.ToDouble(); //计算净重,毛重-皮重=净重,如果净重小于等于0则不上报保存 returndata.Weight = (returndata.Weight - device.Tare.ToDouble()).ToDouble(2); if (returndata.Weight <= 0) { _loggerService.AddLogger($"{data.ECode},{device.Name},重量小于等于0:{returndata.ToJson()}", 1); return new ResultInfo(ResultState.FAIL, "无效的重量"); } //检查是否为15分钟内第一次上报 //如果是巴城的设备则不使用这个限制 if (device.Businessid != Guid.Parse("39FCB9DE-404E-68F5-384B-EE2462EAB87C")) { var time15 = DateTime.Now.AddMinutes(-15); if (await dbClient.Queryable().AnyAsync(x => x.DeviceId == device.Id && x.LastTrash == returndata.trash && x.LastHeartTime > time15)) { _loggerService.AddLogger($"{data.ECode},{device.Name},重复垃圾桶编号的数据:{returndata.ToJson()}", 1); return new ResultInfo(ResultState.FAIL, "15分钟内同一垃圾桶编号上报"); } } returndata.IsSuccessed = true; //记录数据 data.IMEI = data.IMEI.ToStr(); data.ICCID = data.ICCID.ToStr(); data.IMSI = data.IMSI.ToStr(); await _capBus.PublishAsync("result.service.insert", new ResultS2SDto { BusinessId = device.Businessid, DeviceId = device.Id, gslq = data.GSLQ, iccid = data.ICCID, imei = data.IMEI, imsi = data.IMSI, LastHeartTime = device.LastHeartTime, latitude = data.Latitude, longtitude = data.Longitude, ResultId = returndata.ResultId, Tare = device.Tare, trash = returndata.trash, wastetype = type, weight = weight }); } else { _loggerService.AddLogger($"{data.ECode},{device.Name},协议格式不正确:{data.ToJson()}", 1); return new ResultInfo(ResultState.FAIL, "协议格式不正确"); } int timestamp = _suZhouService.GetUTCTimestamp(); int noncestr = _suZhouService.GetNonce(); returndata.DeviceId = devicesecret.DevId; returndata.noncestr = noncestr; returndata.timestamp = timestamp; returndata.Secret = devicesecret.Secret; returndata.SecretHash = devicesecret.SecretHash; returndata.ScanningTime = timestamp; string[] paramlist = new string[] { returndata.Weight.ToString(),returndata.trash,returndata.type.ToString(),returndata.ScanningTime.ToString(),returndata.status.ToString() }; returndata.sign = _suZhouService.GetUserApiSign(returndata.Secret, paramlist); _loggerService.AddLogger($"{data.ECode},{device.Name},发送的数据:{returndata.ToJson()}", 1); return new ResultInfo(ResultState.SUCCESS, "success", returndata); } } /// /// 16进制转汉字 /// /// /// private string GetChsFromHex(string hex) { if (hex == null) return ""; if (hex.Length % 2 != 0) { hex += "20";//空格 } // 需要将 hex 转换成 byte 数组。 byte[] bytes = new byte[hex.Length / 2]; for (int i = 0; i < bytes.Length; i++) { try { // 每两个字符是一个 byte。 bytes[i] = byte.Parse(hex.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber); } catch { } } // 获得 GB2312,Chinese Simplified。 Encoding chs = Encoding.GetEncoding("gb2312"); return chs.GetString(bytes); } /// /// 16进制转10进制 /// /// /// public long HextToDec(string hex) { char[] nums = hex.ToCharArray(); long total = 0; try { for (int i = 0; i < nums.Length; i++) { String strNum = nums[i].ToString().ToUpper(); switch (strNum) { case "A": strNum = "10"; break; case "B": strNum = "11"; break; case "C": strNum = "12"; break; case "D": strNum = "13"; break; case "E": strNum = "14"; break; case "F": strNum = "15"; break; default: break; } double power = Math.Pow(16, Convert.ToDouble(nums.Length - i - 1)); total += Convert.ToInt64(strNum) * Convert.ToInt64(power); } } catch (System.Exception ex) { string strErorr = ex.ToString(); return 0; } return total; } /// /// 心跳数据上报 /// /// /// public async Task PostHeartAsync(DevHeartRequestDto data) { if (!await dbClient.Queryable().AnyAsync(x => x.Ecode == data.ECode)) { return new ResultInfo(ResultState.FAIL, "设备未找到"); } await _capBus.PublishAsync("device.service.postheart", data); return new ResultInfo(ResultState.SUCCESS, "success"); } /// /// 获取设备注册信息,第一次开机使用 /// /// /// public async Task RegInfoAsync(string ecode) { var device = await dbClient.Queryable().FirstAsync(x => x.Ecode == ecode); if (device == null) { return new ResultInfo(ResultState.FAIL, "设备未找到", new DevRegInfoResponseDto()); } //更新开机时间 await _capBus.PublishAsync("device.service.update", device.Id); var data = new DevRegInfoResponseDto { status = 0, WebSocketUrl = WebSocketUrl }; //获取授权信息 var devicesecret = await dbClient.Queryable().FirstAsync(x => x.DeviceId == device.Id); if (devicesecret != null && !string.IsNullOrEmpty(devicesecret.Secret) && !string.IsNullOrEmpty(devicesecret.SecretHash) && !string.IsNullOrEmpty(devicesecret.DevId)) { data.timestamp = _suZhouService.GetUTCTimestamp(); data.noncestr = _suZhouService.GetNonce(); data.UserId = UserId; data.Secret = devicesecret.Secret; data.SecretHash = devicesecret.SecretHash; data.DeviceId = devicesecret.DevId.ToString(); } return new ResultInfo(ResultState.SUCCESS, "success", data); } private int TrashType(string type) { if (type == "厨余垃圾") return 1; else if (type == "可回收物") return 2; else if (type == "有害垃圾") return 3; else if (type == "其他垃圾") return 4; else return 0; } private int GetTimestamp(DateTime time) { DateTime dateTimeStart = TimeZoneInfo.ConvertTimeToUtc(new DateTime(1970, 1, 1, 8, 0, 0)); int timestamp = Convert.ToInt32((time - dateTimeStart).TotalSeconds); return timestamp; } /// /// 字节数组转16进制 /// /// /// private string BytesToHexStr(byte[] bt) { string returnStr = ""; if (bt != null) { for (int i = 0; i < bt.Length; i++) { returnStr += bt[i].ToString("X2"); } } return returnStr; } /// /// 更新设备版本信息 /// /// /// public async Task UpdateVersionAsync(DeviceVerS2SDto data) { await _capBus.PublishAsync("device.service.updatever", data); } /// /// 通过ailink wifi模式发送的数据 /// /// /// public async Task WifiPostAsync(WifiRequestC2SDto data) { //记录发送过来的数据 _loggerService.AddLogger($"wifi发送原始数据,data={data.ToJson()}", 3); var successdata = Encoding.UTF8.GetBytes("A901019A"); var returndata = new { status = "1", data = Convert.ToBase64String(successdata) }; //数据解析 var result = AnalyProto(data); if (!result.ischecked) { return returndata; } if (result.databyte.Length == 0) { return returndata; } //小于14则表示结束 if (result.databyte.Length < 14) { var databytes = await _cahce.GetAsync(result.sn); if (databytes != null && databytes.Length > 0) { await _cahce.RemoveAsync(result.sn); byte[] newval = new byte[databytes.Length + result.databyte.Length]; databytes.CopyTo(newval, 0); result.databyte.CopyTo(newval, databytes.Length); //解析协议,IC卡数据@垃圾桶编号@厨余垃圾@7.91 var datahex = BytesToHexStr(databytes); datahex = datahex.Replace(" ", ""); //收到的为16进制,对数据进行解析,0-4预留,5-垃圾种类,6-垃圾桶大小,7-@,8-12垃圾桶编号,13@,14-21垃圾种类汉字,22@,23-结束重量 var trashhex = datahex.Substring(16, 10); //垃圾桶编号 var typehex = datahex.Substring(28, 16); //垃圾种类 var size = datahex.Substring(12, 2);//桶大小,30-小桶,31-大桶 var weighthex = datahex.Substring(46, datahex.Length - 46); string trashno = HextToDec(trashhex).ToString(); //垃圾桶编号使用10进制 var wastetype = GetChsFromHex(typehex); var weight = GetChsFromHex(weighthex); await _resultService.InsertResultByWifiAsync(new WifiPackage { sn = result.sn, WasteType = wastetype, Weight = weight, trashcode = trashno, size = size, IsHeart = result.IsHeart }); } } else { var val = await _cahce.GetAsync(result.sn); var time = new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(30)); if (val != null && val.Length > 0) { byte[] newval = new byte[val.Length + result.databyte.Length]; val.CopyTo(newval, 0); result.databyte.CopyTo(newval, val.Length); await _cahce.SetAsync(result.sn, newval, time); } else { await _cahce.SetAsync(result.sn, result.databyte, time); } } return returndata; } /// /// wifi数据解析 /// /// /// private WifiRequestS2SDto AnalyProto(WifiRequestC2SDto data) { WifiRequestS2SDto dto = new WifiRequestS2SDto(); //检查是否为有效的base64数据 if (!AESEncrypt.IsBase64(data.@params)) { dto.data = data.@params; return dto; } //数据为base64加密 byte[] resultByte = Convert.FromBase64String(data.@params); dto.data = BytesToHexStr(resultByte); //574D07884A1882FCC2FE346BA1D80E 00 A903000D0A1A9A _loggerService.AddLogger($"wifi结果推送,解析出的数据:{dto.data}", 3); int len = resultByte.Count();//数据总长度 //长度格式是否正确,固定长度为36位 if (len < 23) { return dto; } //前15个字节为sn。中间以 00 分隔。后面为测量结果数据 A9 03 00 0D 0A 1A 9A int i = 0; byte[] snbyte = new byte[15]; for (i = 0; i < 15; i++) { snbyte[i] = resultByte[i]; } dto.sn = BytesToHexStr(snbyte).Replace(" ", ""); dto.splitstr = ByteToHexStr(resultByte[i]); i++; dto.Header = ByteToHexStr(resultByte[i]);//固定为A9 if (dto.Header != "A9") //固定为A9 { return dto; } string heartstr = ByteToHexStr(resultByte[i + 2]);//是否为测量数据 if (heartstr != "01") { dto.IsHeart = true; } dto.ischecked = true; string datalenstr = ByteToHexStr(resultByte[i + 1]); //数据体长度 int datalen = HextToDec(datalenstr).ToInt(); i += 3; dto.databyte = new byte[datalen - 1]; for (int j = 0; j < datalen; j++) { if (i < len - 2) { dto.databyte[j] = resultByte[i]; } i++; } return dto; } /// /// 字节转16进制 /// /// /// private string ByteToHexStr(byte bt) { return ((int)bt).ToString("X2"); } } }