Showing
25 changed files
with
1452 additions
and
118 deletions
@@ -22,19 +22,18 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; | @@ -22,19 +22,18 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; | ||
22 | import org.thingsboard.server.common.data.yunteng.common.AddGroup; | 22 | import org.thingsboard.server.common.data.yunteng.common.AddGroup; |
23 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; | 23 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; |
24 | import org.thingsboard.server.common.data.yunteng.common.UpdateGroup; | 24 | import org.thingsboard.server.common.data.yunteng.common.UpdateGroup; |
25 | +import org.thingsboard.server.common.data.yunteng.core.exception.ThingsKitException; | ||
25 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | 26 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; |
26 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | 27 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; |
27 | import org.thingsboard.server.common.data.yunteng.dto.*; | 28 | import org.thingsboard.server.common.data.yunteng.dto.*; |
28 | import org.thingsboard.server.common.data.yunteng.dto.thingsmodel.ImportThingsModelDTO; | 29 | import org.thingsboard.server.common.data.yunteng.dto.thingsmodel.ImportThingsModelDTO; |
29 | -import org.thingsboard.server.common.data.yunteng.enums.FunctionTypeEnum; | ||
30 | -import org.thingsboard.server.common.data.yunteng.enums.ImportEnum; | ||
31 | -import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; | ||
32 | -import org.thingsboard.server.common.data.yunteng.enums.StatusEnum; | 30 | +import org.thingsboard.server.common.data.yunteng.enums.*; |
33 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; | 31 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; |
34 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | 32 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
35 | import org.thingsboard.server.controller.BaseController; | 33 | import org.thingsboard.server.controller.BaseController; |
36 | import org.thingsboard.server.dao.yunteng.service.ThingsModelService; | 34 | import org.thingsboard.server.dao.yunteng.service.ThingsModelService; |
37 | import org.thingsboard.server.dao.yunteng.service.TkDeviceProfileService; | 35 | import org.thingsboard.server.dao.yunteng.service.TkDeviceProfileService; |
36 | +import org.thingsboard.server.utils.ImportModbusUtils; | ||
38 | 37 | ||
39 | import java.io.File; | 38 | import java.io.File; |
40 | import java.io.IOException; | 39 | import java.io.IOException; |
@@ -42,6 +41,7 @@ import java.io.InputStream; | @@ -42,6 +41,7 @@ import java.io.InputStream; | ||
42 | import java.util.HashMap; | 41 | import java.util.HashMap; |
43 | import java.util.List; | 42 | import java.util.List; |
44 | import java.util.Map; | 43 | import java.util.Map; |
44 | +import java.util.regex.Pattern; | ||
45 | 45 | ||
46 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*; | 46 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*; |
47 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.ORDER_TYPE; | 47 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.ORDER_TYPE; |
@@ -234,16 +234,221 @@ public class ThingsModelController extends BaseController { | @@ -234,16 +234,221 @@ public class ThingsModelController extends BaseController { | ||
234 | return thingsModelService.saveOrUpdate(thingsModelDTO); | 234 | return thingsModelService.saveOrUpdate(thingsModelDTO); |
235 | } | 235 | } |
236 | 236 | ||
237 | + @GetMapping("/download/template") | ||
238 | + @ApiOperation("excel模板下载") | ||
239 | + public ResponseEntity<Resource> downloadTemplate(String type) throws IOException { | ||
240 | + String name = "template"+ File.separator; | ||
241 | + if(StringUtils.isNotEmpty(type)&&type.equals("modbus")){ | ||
242 | + name = name +"modbusTemplates.xls"; | ||
243 | + }else{ | ||
244 | + name = name+"template.xls"; | ||
245 | + } | ||
246 | + InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(name); | ||
247 | + if(inputStream!=null){ | ||
248 | + HttpHeaders headers = new HttpHeaders(); | ||
249 | + headers.add(HttpHeaders.CONTENT_DISPOSITION,"attachment;filename=物模型属性导入模板.xls"); | ||
250 | + return ResponseEntity.ok() | ||
251 | + .headers(headers) | ||
252 | + .contentType(MediaType.APPLICATION_OCTET_STREAM) | ||
253 | + .body(new InputStreamResource(inputStream)); | ||
254 | + }else { | ||
255 | + return null; | ||
256 | + } | ||
257 | + } | ||
258 | + | ||
259 | + | ||
260 | + @PostMapping("/batch/get_tsl") | ||
261 | + @ApiOperation("获取多个产品的物模型数据") | ||
262 | + public ResponseEntity<List<BatchDeviceProfileInfoDTO>> getDeviceProfilesTSL(@RequestBody DeviceProfileTSLDTO profileTSLDTO) | ||
263 | + throws ThingsboardException { | ||
264 | + if(null == profileTSLDTO.getDeviceProfileIds() || null == profileTSLDTO.getFunctionTypeEnum() || | ||
265 | + profileTSLDTO.getDeviceProfileIds().isEmpty()){ | ||
266 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | ||
267 | + } | ||
268 | + return ResponseEntity.ok(thingsModelService.getDeviceProfileTSLInfoByIds(getCurrentUser().getCurrentTenantId(), | ||
269 | + profileTSLDTO.getDeviceProfileIds(),profileTSLDTO.getFunctionTypeEnum())); | ||
270 | + } | ||
271 | + | ||
237 | @ApiOperation(value = "通过excel导入物模型") | 272 | @ApiOperation(value = "通过excel导入物模型") |
238 | @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN'},{'api:yt:things_model:excel_import'})") | 273 | @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN'},{'api:yt:things_model:excel_import'})") |
239 | @PostMapping("/csvImport") | 274 | @PostMapping("/csvImport") |
240 | - public ResponseResult<String> csvImport(String deviceProfileId, String categoryId, | 275 | + public ResponseResult<String> csvImport(String deviceProfileId, String categoryId,String type, |
241 | @RequestPart("file") MultipartFile file) throws Exception { | 276 | @RequestPart("file") MultipartFile file) throws Exception { |
242 | if(file.isEmpty()){ | 277 | if(file.isEmpty()){ |
243 | - return null; | 278 | + throw new ThingsKitException(ErrorMessage.INVALID_PARAMETER); |
244 | } | 279 | } |
280 | + try{ | ||
245 | Workbook work = WorkbookFactory.create(file.getInputStream()); | 281 | Workbook work = WorkbookFactory.create(file.getInputStream()); |
282 | + String tenantId = getCurrentUser().getCurrentTenantId(); | ||
283 | + if(type.equals("modbus")){ | ||
284 | + return importModbus(deviceProfileId,categoryId,file,tenantId,work); | ||
285 | + }else{ | ||
286 | + return importModel(deviceProfileId,categoryId,file,tenantId,work); | ||
287 | + } | ||
288 | + }catch (Exception e){ | ||
289 | + throw new TkDataValidationException(ErrorMessage.IMPORT_ERROR.getMessage()); | ||
290 | + } | ||
291 | + } | ||
292 | + | ||
293 | + | ||
294 | + ResponseResult<String> importModbus(String deviceProfileId, String categoryId,MultipartFile file,String tenantId, Workbook work) throws IOException { | ||
295 | + | ||
296 | + Sheet sheet = work.getSheetAt(0); | ||
297 | + int succeed = 0;//成功数量 | ||
298 | + int failed =0;//失败数量 | ||
299 | + StringBuffer failedString = new StringBuffer(); | ||
300 | + String excel = sheet.getRow(0).getCell(2).toString(); | ||
301 | + if(!excel.equals("MODBUS_RTU操作类型*")){ | ||
302 | + throw new TkDataValidationException(ErrorMessage.IMPORT_ERROR.getMessage()); | ||
303 | + } | ||
304 | + for(int i = 1; i<=sheet.getPhysicalNumberOfRows(); i++) { | ||
305 | + Row row = sheet.getRow(i); | ||
306 | + if(row!=null){ | ||
307 | + String functionName = row.getCell(0).toString();//功能名称 | ||
308 | + if(StringUtils.isEmpty(functionName)){ | ||
309 | + failed++; | ||
310 | + failedString.append("第"+i+"条,功能名称未填;"); | ||
311 | + continue; | ||
312 | + } | ||
313 | + String identifier = row.getCell(1).toString();//标识符* | ||
314 | + if(StringUtils.isEmpty(identifier)){ | ||
315 | + failed++; | ||
316 | + failedString.append("第"+i+"条,标识符未填;"); | ||
317 | + continue; | ||
318 | + } | ||
319 | + Boolean identifierState = thingsModelService.getByIdentifier(tenantId,deviceProfileId,categoryId,identifier); | ||
320 | + if(!identifierState){ | ||
321 | + failed++; | ||
322 | + failedString.append("第"+i+"条,标识符已存在;"); | ||
323 | + continue; | ||
324 | + } | ||
325 | + ThingsModelDTO dto = new ThingsModelDTO(); | ||
326 | + | ||
327 | + String extensionJson =""; | ||
328 | + String json =""; | ||
329 | + | ||
330 | + if(identifier.equals("source")){ | ||
331 | + dto.setAccessMode("r"); | ||
332 | + extensionJson ="{\"writeOnly\":true}"; | ||
333 | + json ="{\"dataType\":{\"type\":\"STRING\",\"specs\":{}}}"; | ||
334 | + }else{ | ||
335 | + String operationType = row.getCell(2).toString();//MODBUS_RTU操作类型* | ||
336 | + if(StringUtils.isEmpty(operationType)){ | ||
337 | + failed++; | ||
338 | + failedString.append("第"+i+"条,MODBUS_RTU操作类型未填;"); | ||
339 | + continue; | ||
340 | + } | ||
341 | + String originalDataType = row.getCell(3).toString();//MODBUS_RTU数据类型* | ||
342 | + if(StringUtils.isEmpty(originalDataType)){ | ||
343 | + failed++; | ||
344 | + failedString.append("第"+i+"条,MODBUS_RTU数据类型未填;"); | ||
345 | + continue; | ||
346 | + } | ||
347 | + operationType =OperationTypeEnum.getName(operationType); | ||
348 | + originalDataType = AttributeSourceDataTypeEnum.getName(originalDataType); | ||
349 | + if(!ImportModbusUtils.operationTypeAndOriginalDataTypeStatus(operationType, originalDataType)){ | ||
350 | + failed++; | ||
351 | + failedString.append("第"+i+"条,操作类型与数据类型不匹配;"); | ||
352 | + continue; | ||
353 | + } | ||
354 | + | ||
355 | + | ||
356 | + String registerType = row.getCell(5).toString();//寄存器类型 | ||
357 | + String registerAddress =row.getCell(6).toString();//寄存器地址 | ||
358 | + if(StringUtils.isEmpty(registerType)){ | ||
359 | + failed++; | ||
360 | + failedString.append("第"+i+"条,寄存器类型未填;"); | ||
361 | + continue; | ||
362 | + } | ||
363 | + //转换校验输入的字符 | ||
364 | + if(registerType.equals("HEX")){ | ||
365 | + String hexPattern="^[0-9A-Fa-f]+$"; | ||
366 | + if(!Pattern.matches(hexPattern,registerAddress)){ | ||
367 | + failedString.append("第"+i+"条,寄存器地址格式不正确;"); | ||
368 | + continue; | ||
369 | + } | ||
370 | + } | ||
371 | + if(registerType.equals("DEC")){ | ||
372 | + try{ | ||
373 | + registerAddress = Integer.toHexString(Integer.parseInt(registerAddress)); | ||
374 | + } catch (NumberFormatException e) { | ||
375 | + failedString.append("第"+i+"条,寄存器地址格式不正确;"); | ||
376 | + continue; | ||
377 | + } | ||
378 | + } | ||
379 | + | ||
380 | + String bitMask = row.getCell(7)==null?null:row.getCell(7).toString();//比特位* | ||
381 | + if(originalDataType.equals("位")&&StringUtils.isEmpty(bitMask)){ | ||
382 | + failed++; | ||
383 | + failedString.append("第"+i+"条,比特位置(数据类型为位时必填);"); | ||
384 | + continue; | ||
385 | + } | ||
386 | + if(StringUtils.isEmpty(bitMask)){ | ||
387 | + bitMask="7"; | ||
388 | + } | ||
389 | + | ||
390 | + String scaling = row.getCell(8)==null?"1":row.getCell(8).toString();//缩放因子 | ||
391 | + String registerCount = row.getCell(9)==null?"":row.getCell(9).toString();//寄存器个数 | ||
392 | + | ||
393 | + if(originalDataType.equals("字符串")&&StringUtils.isEmpty(registerCount)){ | ||
394 | + failed++; | ||
395 | + failedString.append("第"+i+"条,寄存器个数(数据类型为字符串时必填);"); | ||
396 | + continue; | ||
397 | + } | ||
398 | + | ||
399 | + | ||
400 | + String unit =""; | ||
401 | + String valueRange =""; | ||
402 | + String dataType = ImportModbusUtils.getOriginalDataTypeIsDateType(originalDataType); | ||
403 | + String cell4 = row.getCell(4)==null?null:row.getCell(4).toString();//单位 | ||
404 | + if(cell4!=null){ | ||
405 | + String[] dws = cell4.split("/"); | ||
406 | + unit = "\"unit\":\""+dws[1]+"\",\"unitName\":\""+cell4+"\""; | ||
407 | + } | ||
408 | + if(unit.equals("")){ | ||
409 | + valueRange = ImportModbusUtils.getOriginalDataTypeIsValueRange(originalDataType); | ||
410 | + }else{ | ||
411 | + valueRange = ","+ImportModbusUtils.getOriginalDataTypeIsValueRange(originalDataType); | ||
412 | + } | ||
413 | + json ="{\"dataType\":{\"type\":\""+dataType+"\",\"specs\":{"+unit+valueRange+"}}}"; | ||
414 | + extensionJson = | ||
415 | + "{\"writeOnly\":"+ImportModbusUtils.getOperationTypeIsWriteOnly(operationType)+ | ||
416 | + ",\"bitMask\":"+bitMask+",\"operationType\":\""+operationType+"\",\"originalDataType\":\""+originalDataType+ | ||
417 | + "\",\"registerAddress\":\""+registerAddress+"\",\"scaling\":"+scaling+"}"; | ||
418 | + if(!registerCount.equals("")){ | ||
419 | + extensionJson = extensionJson+",\"registerCount\":"+registerCount; | ||
420 | + } | ||
421 | + dto.setAccessMode(ImportModbusUtils.getOperationTypeIsRw(operationType)); | ||
422 | + } | ||
423 | + | ||
424 | + dto.setDeviceProfileId(deviceProfileId); | ||
425 | + dto.setCategoryId(categoryId); | ||
426 | + dto.setFunctionType(FunctionTypeEnum.properties); | ||
427 | + dto.setFunctionName(functionName); | ||
428 | + dto.setIdentifier(identifier); | ||
429 | + JsonNode functionJson = JacksonUtil.toJsonNode(json); | ||
430 | + JsonNode extensionDesc = JacksonUtil.toJsonNode(extensionJson); | ||
431 | + dto.setFunctionJson(functionJson); | ||
432 | + dto.setExtensionDesc(extensionDesc); | ||
433 | + dto.setRemark(row.getCell(10)==null?null:row.getCell(10).toString()); | ||
434 | + dto.setTenantId(tenantId); | ||
435 | + if(dto.getCategoryId()!=null){ | ||
436 | + thingsModelService.categorySaveOrUpdate(dto); | ||
437 | + }else{ | ||
438 | + thingsModelService.saveOrUpdate(dto); | ||
439 | + } | ||
440 | + succeed++; | ||
441 | + } | ||
442 | + } | ||
443 | + return ResponseResult.success("操作成功,导入"+succeed+"条;失败"+failed+"条;失败原因:"+failedString); | ||
444 | + } | ||
445 | + | ||
446 | + ResponseResult<String> importModel(String deviceProfileId, String categoryId,MultipartFile file,String tenantId, Workbook work) throws IOException { | ||
246 | Sheet sheet = work.getSheetAt(0); | 447 | Sheet sheet = work.getSheetAt(0); |
448 | + String excel = sheet.getRow(0).getCell(2).toString(); | ||
449 | + if(!excel.equals("数据类型*")){ | ||
450 | + return ResponseResult.failed(500,"操作失败,请使用下载模板excel重新导入"); | ||
451 | + } | ||
247 | int succeed = 0;//成功数量 | 452 | int succeed = 0;//成功数量 |
248 | int failed =0;//失败数量 | 453 | int failed =0;//失败数量 |
249 | StringBuffer failedString = new StringBuffer(); | 454 | StringBuffer failedString = new StringBuffer(); |
@@ -282,8 +487,6 @@ public class ThingsModelController extends BaseController { | @@ -282,8 +487,6 @@ public class ThingsModelController extends BaseController { | ||
282 | continue; | 487 | continue; |
283 | } | 488 | } |
284 | } | 489 | } |
285 | - | ||
286 | - String tenantId = getCurrentUser().getCurrentTenantId(); | ||
287 | Boolean identifierState = thingsModelService.getByIdentifier(tenantId,deviceProfileId,categoryId,identifier); | 490 | Boolean identifierState = thingsModelService.getByIdentifier(tenantId,deviceProfileId,categoryId,identifier); |
288 | if(!identifierState){ | 491 | if(!identifierState){ |
289 | failed++; | 492 | failed++; |
@@ -349,32 +552,4 @@ public class ThingsModelController extends BaseController { | @@ -349,32 +552,4 @@ public class ThingsModelController extends BaseController { | ||
349 | } | 552 | } |
350 | return ResponseResult.success("操作成功,导入"+succeed+"条;失败"+failed+"条;失败原因:"+failedString.toString()); | 553 | return ResponseResult.success("操作成功,导入"+succeed+"条;失败"+failed+"条;失败原因:"+failedString.toString()); |
351 | } | 554 | } |
352 | - | ||
353 | - @GetMapping("/downloadTemplate") | ||
354 | - @ApiOperation("excel模板下载") | ||
355 | - public ResponseEntity<Resource> downloadTemplate() throws IOException { | ||
356 | - InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template"+ File.separator +"template.xls"); | ||
357 | - if(inputStream!=null){ | ||
358 | - HttpHeaders headers = new HttpHeaders(); | ||
359 | - headers.add(HttpHeaders.CONTENT_DISPOSITION,"attachment;filename=物模型属性导入模板.xls"); | ||
360 | - return ResponseEntity.ok() | ||
361 | - .headers(headers) | ||
362 | - .contentType(MediaType.APPLICATION_OCTET_STREAM) | ||
363 | - .body(new InputStreamResource(inputStream)); | ||
364 | - }else { | ||
365 | - return null; | ||
366 | - } | ||
367 | - } | ||
368 | - | ||
369 | - @PostMapping("/batch/get_tsl") | ||
370 | - @ApiOperation("获取多个产品的物模型数据") | ||
371 | - public ResponseEntity<List<BatchDeviceProfileInfoDTO>> getDeviceProfilesTSL(@RequestBody DeviceProfileTSLDTO profileTSLDTO) | ||
372 | - throws ThingsboardException { | ||
373 | - if(null == profileTSLDTO.getDeviceProfileIds() || null == profileTSLDTO.getFunctionTypeEnum() || | ||
374 | - profileTSLDTO.getDeviceProfileIds().isEmpty()){ | ||
375 | - throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | ||
376 | - } | ||
377 | - return ResponseEntity.ok(thingsModelService.getDeviceProfileTSLInfoByIds(getCurrentUser().getCurrentTenantId(), | ||
378 | - profileTSLDTO.getDeviceProfileIds(),profileTSLDTO.getFunctionTypeEnum())); | ||
379 | - } | ||
380 | } | 555 | } |
1 | +package org.thingsboard.server.utils; | ||
2 | + | ||
3 | +import org.thingsboard.server.common.data.yunteng.enums.AttributeSourceDataTypeEnum; | ||
4 | +import org.thingsboard.server.common.data.yunteng.enums.OperationTypeEnum; | ||
5 | + | ||
6 | +public class ImportModbusUtils { | ||
7 | + | ||
8 | + public static Boolean operationTypeAndOriginalDataTypeStatus(String operationType, String OriginalDataType){ | ||
9 | + if((operationType.equals(OperationTypeEnum.inputStatus_r_02.name())|| | ||
10 | + operationType.equals(OperationTypeEnum.coilStatus_r_01.name())|| | ||
11 | + operationType.equals(OperationTypeEnum.coilStatus_rw_01_05.name())|| | ||
12 | + operationType.equals(OperationTypeEnum.coilStatus_rw_01_0F.name())|| | ||
13 | + operationType.equals(OperationTypeEnum.coilStatus_w_05.name())|| | ||
14 | + operationType.equals(OperationTypeEnum.coilStatus_w_0F.name()))&&!OriginalDataType.equals(AttributeSourceDataTypeEnum.BOOLEAN.name())){ | ||
15 | + return false; | ||
16 | + } | ||
17 | + if((operationType.equals(OperationTypeEnum.holdingRegister_rw_03_06.name())|| | ||
18 | + operationType.equals(OperationTypeEnum.holdingRegister_w_06.name()))&& | ||
19 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.INT16_AB.name())&& | ||
20 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.INT16_BA.name())&& | ||
21 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT16_AB.name())&& | ||
22 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT16_BA.name())&& | ||
23 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.BOOLEAN.name())&& | ||
24 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.BITS.name()) | ||
25 | + ){ | ||
26 | + return false; | ||
27 | + } | ||
28 | + return true; | ||
29 | + } | ||
30 | + | ||
31 | + | ||
32 | + public static String getOriginalDataTypeIsDateType(String OriginalDataType){ | ||
33 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.FLOAT_AB_CD.name())|| | ||
34 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.FLOAT_CD_AB.name())|| | ||
35 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.FLOAT_BA_DC.name())|| | ||
36 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.FLOAT_DC_BA.name())|| | ||
37 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.DOUBLE.name())){ | ||
38 | + return "DOUBLE"; | ||
39 | + } | ||
40 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.BOOLEAN.name())|| | ||
41 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.BITS.name())){ | ||
42 | + return "BOOLEAN"; | ||
43 | + } | ||
44 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.STRING.name())){ | ||
45 | + return "STRING"; | ||
46 | + } | ||
47 | + return "INT"; | ||
48 | + } | ||
49 | + | ||
50 | + public static String getOriginalDataTypeIsValueRange(String OriginalDataType){ | ||
51 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.INT16_AB.name())|| | ||
52 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.INT16_BA.name())){ | ||
53 | + return "\"valueRange\":{\"min\":-32768,\"max\":32767}"; | ||
54 | + } | ||
55 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT16_AB.name())|| | ||
56 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT16_BA.name())){ | ||
57 | + return "\"valueRange\":{\"min\":0,\"max\":65535}"; | ||
58 | + } | ||
59 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT32_AB_CD.name())|| | ||
60 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT32_CD_AB.name())|| | ||
61 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT32_BA_DC.name())|| | ||
62 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT32_DC_BA.name())){ | ||
63 | + return "\"valueRange\":{\"min\":0,\"max\":4294967295}"; | ||
64 | + } | ||
65 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.STRING.name())){ | ||
66 | + return ""; | ||
67 | + } | ||
68 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.BOOLEAN.name())|| | ||
69 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.BITS.name())){ | ||
70 | + return "\"valueRange\":{\"min\":0,\"max\":1}"; | ||
71 | + } | ||
72 | + return "\"valueRange\":{\"min\":-2147483648,\"max\":2147483647}"; | ||
73 | + } | ||
74 | + | ||
75 | + public static String getOperationTypeIsRw(String operationType){ | ||
76 | + String []rw = operationType.split("_"); | ||
77 | + return rw[1].equals("r")?"r":"rw"; | ||
78 | + } | ||
79 | + | ||
80 | + public static Boolean getOperationTypeIsWriteOnly(String operationType){ | ||
81 | + String rw = getOperationTypeIsRw(operationType); | ||
82 | + if(rw.equals("r")){//只读为false | ||
83 | + return false; | ||
84 | + } | ||
85 | + return true; | ||
86 | + } | ||
87 | +} |
No preview for this file type
@@ -3,6 +3,7 @@ package org.thingsboard.server.common.data.device.profile; | @@ -3,6 +3,7 @@ package org.thingsboard.server.common.data.device.profile; | ||
3 | import lombok.Data; | 3 | import lombok.Data; |
4 | import org.thingsboard.server.common.data.DeviceTransportType; | 4 | import org.thingsboard.server.common.data.DeviceTransportType; |
5 | import org.thingsboard.server.common.data.validation.NoXss; | 5 | import org.thingsboard.server.common.data.validation.NoXss; |
6 | +import org.thingsboard.server.common.data.yunteng.enums.ProtocolAnalysisEnum; | ||
6 | 7 | ||
7 | @Data | 8 | @Data |
8 | public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { | 9 | public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { |
@@ -26,6 +27,12 @@ public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTr | @@ -26,6 +27,12 @@ public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTr | ||
26 | @NoXss | 27 | @NoXss |
27 | private String downScriptId; | 28 | private String downScriptId; |
28 | 29 | ||
30 | + /** | ||
31 | + * 协议: 默认自定义 | ||
32 | + */ | ||
33 | + @NoXss | ||
34 | + private ProtocolAnalysisEnum protocol = ProtocolAnalysisEnum.CUSTOM; | ||
35 | + | ||
29 | @Override | 36 | @Override |
30 | public DeviceTransportType getType() { | 37 | public DeviceTransportType getType() { |
31 | return DeviceTransportType.TCP; | 38 | return DeviceTransportType.TCP; |
@@ -37,7 +37,36 @@ public interface FastIotConstants { | @@ -37,7 +37,36 @@ public interface FastIotConstants { | ||
37 | String ORGANIZATION = "organization"; | 37 | String ORGANIZATION = "organization"; |
38 | String SCENE_REACT = "sceneReact"; | 38 | String SCENE_REACT = "sceneReact"; |
39 | } | 39 | } |
40 | - | 40 | + interface ModBusRTU{ |
41 | + /** | ||
42 | + * 原始数据类型 | ||
43 | + */ | ||
44 | + String ORIGINAL_DATA_TYPE = "originalDataType"; | ||
45 | + /** | ||
46 | + * 寄存器地址 | ||
47 | + */ | ||
48 | + String REGISTER_ADDRESS = "registerAddress"; | ||
49 | + | ||
50 | + /** | ||
51 | + * 操作功能码类型 | ||
52 | + */ | ||
53 | + String OPERATION_TYPE = "operationType"; | ||
54 | + | ||
55 | + /** | ||
56 | + * 缩放因子 | ||
57 | + */ | ||
58 | + String SCALE_FACTOR = "scaling"; | ||
59 | + | ||
60 | + /** | ||
61 | + * 原始hex的标识符 | ||
62 | + */ | ||
63 | + String SOURCE_DATA_IDENTIFY = "source"; | ||
64 | + | ||
65 | + /** | ||
66 | + * 比特位置 | ||
67 | + */ | ||
68 | + String BIT_MASK = "bitMask"; | ||
69 | + } | ||
41 | interface TBCacheConfig { | 70 | interface TBCacheConfig { |
42 | String TB_CACHE_CONFIG_KEY = "TB_CONNECT_CACHE"; | 71 | String TB_CACHE_CONFIG_KEY = "TB_CONNECT_CACHE"; |
43 | String EXISTING_TENANT = "EXISTING_TENANT"; | 72 | String EXISTING_TENANT = "EXISTING_TENANT"; |
@@ -120,6 +120,7 @@ public enum ErrorMessage { | @@ -120,6 +120,7 @@ public enum ErrorMessage { | ||
120 | EZVIZ_API_ERROR(400095,"荧石视频获取TokenAPI调用失败【%s】,错误码【%s】"), | 120 | EZVIZ_API_ERROR(400095,"荧石视频获取TokenAPI调用失败【%s】,错误码【%s】"), |
121 | EZVIZ_GET_URL_ERROR(400096,"荧石API调用获取URL失败!!"), | 121 | EZVIZ_GET_URL_ERROR(400096,"荧石API调用获取URL失败!!"), |
122 | IMPORT_TCP_ERROR(400097,"TCP产品不能导入INT,DOUBLE,BOOL,TEXT以外的数据类型属性!!"), | 122 | IMPORT_TCP_ERROR(400097,"TCP产品不能导入INT,DOUBLE,BOOL,TEXT以外的数据类型属性!!"), |
123 | + IMPORT_ERROR(400098,"请使用模板excel重新导入"), | ||
123 | HAVE_NO_PERMISSION(500002,"没有修改权限"), | 124 | HAVE_NO_PERMISSION(500002,"没有修改权限"), |
124 | NOT_ALLOED_ISOLATED_IN_MONOLITH(500003,"【monolith】模式下,不能选择【isolated】类型的租户配置"); | 125 | NOT_ALLOED_ISOLATED_IN_MONOLITH(500003,"【monolith】模式下,不能选择【isolated】类型的租户配置"); |
125 | 126 |
@@ -2,6 +2,7 @@ package org.thingsboard.server.common.data.yunteng.dto; | @@ -2,6 +2,7 @@ package org.thingsboard.server.common.data.yunteng.dto; | ||
2 | 2 | ||
3 | import io.swagger.annotations.ApiModelProperty; | 3 | import io.swagger.annotations.ApiModelProperty; |
4 | import lombok.Data; | 4 | import lombok.Data; |
5 | +import org.thingsboard.server.common.data.yunteng.enums.HexByteOrderEnum; | ||
5 | import org.thingsboard.server.common.data.yunteng.enums.TkModBusCheckType; | 6 | import org.thingsboard.server.common.data.yunteng.enums.TkModBusCheckType; |
6 | import java.io.Serializable; | 7 | import java.io.Serializable; |
7 | import java.util.List; | 8 | import java.util.List; |
@@ -28,4 +29,7 @@ public class TkDeviceRpcDTO implements Serializable { | @@ -28,4 +29,7 @@ public class TkDeviceRpcDTO implements Serializable { | ||
28 | 29 | ||
29 | @ApiModelProperty(value = "寄存器值") | 30 | @ApiModelProperty(value = "寄存器值") |
30 | private List<Integer> registerValues; | 31 | private List<Integer> registerValues; |
32 | + | ||
33 | + @ApiModelProperty(value = "顺序") | ||
34 | + private HexByteOrderEnum hexByteOrderEnum; | ||
31 | } | 35 | } |
@@ -32,6 +32,7 @@ public class TkThingsModel implements Serializable { | @@ -32,6 +32,7 @@ public class TkThingsModel implements Serializable { | ||
32 | 32 | ||
33 | private JsonNode functionJson; | 33 | private JsonNode functionJson; |
34 | 34 | ||
35 | + private JsonNode extensionDesc; | ||
35 | 36 | ||
36 | private Integer status; | 37 | private Integer status; |
37 | 38 |
1 | +package org.thingsboard.server.common.data.yunteng.enums; | ||
2 | + | ||
3 | +/** 物模型的原始数据类型 */ | ||
4 | +public enum AttributeSourceDataTypeEnum { | ||
5 | + /** 16位有符号整数AB */ | ||
6 | + INT16_AB("16位有符号整数AB"), | ||
7 | + /** 16位有符号整数BA */ | ||
8 | + INT16_BA("16位有符号整数BA"), | ||
9 | + /** 16位无符号整数AB */ | ||
10 | + UINT16_AB("16位无符号整数AB"), | ||
11 | + /** 16位无符号整数BA */ | ||
12 | + UINT16_BA("16位无符号整数BA"), | ||
13 | + /** 32位有符号整数AB_CD */ | ||
14 | + INT32_AB_CD("32位有符号整数AB_CD"), | ||
15 | + /** 32位有符号整数CD_AB */ | ||
16 | + INT32_CD_AB("32位有符号整数CD_AB"), | ||
17 | + /** 32位有符号整数BA_DC */ | ||
18 | + INT32_BA_DC("32位有符号整数BA_DC"), | ||
19 | + /** 32位有符号整数DC_BA */ | ||
20 | + INT32_DC_BA("32位有符号整数DC_BA"), | ||
21 | + /** 32位无符号整数AB_CD */ | ||
22 | + UINT32_AB_CD("32位无符号整数AB_CD"), | ||
23 | + /** 32位无符号整数CD_AB */ | ||
24 | + UINT32_CD_AB("32位无符号整数CD_AB"), | ||
25 | + /** 32位无符号整数BA_DC */ | ||
26 | + UINT32_BA_DC("32位无符号整数BA_DC"), | ||
27 | + /** 32位无符号整数DC_BA */ | ||
28 | + UINT32_DC_BA("32位无符号整数DC_BA"), | ||
29 | + /** 单精度浮点型AB_CD */ | ||
30 | + FLOAT_AB_CD("单精度浮点型AB_CD"), | ||
31 | + /** 单精度浮点型CD_AB */ | ||
32 | + FLOAT_CD_AB("单精度浮点型CD_AB"), | ||
33 | + /** 单精度浮点型BA_DC */ | ||
34 | + FLOAT_BA_DC("单精度浮点型BA_DC"), | ||
35 | + /** 单精度浮点型AB_CD */ | ||
36 | + FLOAT_DC_BA("单精度浮点型AB_CD"), | ||
37 | + /** 双精度浮点型 */ | ||
38 | + DOUBLE("双精度浮点型"), | ||
39 | + /** 字符串 */ | ||
40 | + STRING("字符串"), | ||
41 | + /** 布尔型 */ | ||
42 | + BOOLEAN("布尔型"), | ||
43 | + /** 位 */ | ||
44 | + BITS("位"); | ||
45 | + | ||
46 | + String label; | ||
47 | + | ||
48 | + AttributeSourceDataTypeEnum(String label) { | ||
49 | + this.label = label; | ||
50 | + } | ||
51 | + | ||
52 | + public static String getName(String label) { | ||
53 | + for (AttributeSourceDataTypeEnum type : values()) { | ||
54 | + if (type.label.equals(label)) { | ||
55 | + return type.name(); | ||
56 | + } | ||
57 | + } | ||
58 | + return null; | ||
59 | + } | ||
60 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/enums/OperationTypeEnum.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.enums; | ||
2 | + | ||
3 | +/** 物模型的原始操作类型 */ | ||
4 | +public enum OperationTypeEnum { | ||
5 | + inputStatus_r_02("离散量输入_只读0x02"), | ||
6 | + coilStatus_r_01("线圈状态_只读0x01"), | ||
7 | + coilStatus_rw_01_05("线圈状态_读写读取使用0x01写入使用0x05"), | ||
8 | + coilStatus_rw_01_0F("线圈状态_读写读取使用0x01写入使用0x0F"), | ||
9 | + coilStatus_w_05("线圈状态_只写0x05"), | ||
10 | + coilStatus_w_0F("线圈状态_只写0x0F"), | ||
11 | + holdingRegister_r_03("保持寄存器_只读0x03"), | ||
12 | + holdingRegister_rw_03_06("保持寄存器_读写读取使用0x03写入使用0x06"), | ||
13 | + holdingRegister_rw_03_10("保持寄存器_读写读取使用0x03写入使用0x10"), | ||
14 | + holdingRegister_w_06("保持寄存器_只写0x06"), | ||
15 | + holdingRegister_w_10("保持寄存器_只写0x10"), | ||
16 | + inputRegister_r_04("输入寄存器_只读0x04"); | ||
17 | + String label; | ||
18 | + | ||
19 | + OperationTypeEnum(String label) { | ||
20 | + this.label = label; | ||
21 | + } | ||
22 | + public static String getName(String label) { | ||
23 | + for (OperationTypeEnum type : values()) { | ||
24 | + if (type.label.equals(label)) { | ||
25 | + return type.name(); | ||
26 | + } | ||
27 | + } | ||
28 | + return null; | ||
29 | + } | ||
30 | +} |
@@ -9,6 +9,7 @@ public enum TkRpcStatus { | @@ -9,6 +9,7 @@ public enum TkRpcStatus { | ||
9 | EXPIRED("已过期"), | 9 | EXPIRED("已过期"), |
10 | FAILED("响应失败"), | 10 | FAILED("响应失败"), |
11 | DELETED("已删除"); | 11 | DELETED("已删除"); |
12 | + | ||
12 | String label; | 13 | String label; |
13 | 14 | ||
14 | TkRpcStatus(String label) { | 15 | TkRpcStatus(String label) { |
@@ -3,40 +3,41 @@ package org.thingsboard.server.common.data.yunteng.utils; | @@ -3,40 +3,41 @@ package org.thingsboard.server.common.data.yunteng.utils; | ||
3 | import org.thingsboard.server.common.data.yunteng.enums.TkModBusCheckType; | 3 | import org.thingsboard.server.common.data.yunteng.enums.TkModBusCheckType; |
4 | 4 | ||
5 | public class CrcUtils { | 5 | public class CrcUtils { |
6 | -/** | ||
7 | - * crc16 for modbus | ||
8 | - * @param buf buffer to be crc | ||
9 | - * @return crc result word | ||
10 | - */ | ||
11 | - static int alex_crc16(TkModBusCheckType algorithm, byte[] buf) { | ||
12 | - int i, j; | ||
13 | - int c, crc = algorithm.getInit(); | ||
14 | - for (i = 0; i < buf.length; i++) { | ||
15 | - c = buf[i] & 0x00FF;//原始数据高8位和初始值进行异或运算保持不变 | ||
16 | - crc ^= c; | ||
17 | - for (j = 0; j < 8; j++) {//原始数据左移8位 | ||
18 | - if ((crc & 0x0001) != 0) { | ||
19 | - crc >>= 1; | ||
20 | - crc ^= algorithm.getXorIn();//把处理之后的数据和多项式进行模2除法,求得余数 | ||
21 | - } else | ||
22 | - crc >>= 1; | ||
23 | - } | ||
24 | - } | ||
25 | - return (crc); | ||
26 | - } | ||
27 | -/** | ||
28 | - * convert string to buffer and append the crc check word to the end of buffer | ||
29 | - * @param toSend string to be convert | ||
30 | - * @return buffer with crc word, high byte if after low byte according the modbus | ||
31 | - */ | ||
32 | - public static String getSendBuf(TkModBusCheckType algorithm, String toSend) { | ||
33 | - byte[] bb = ByteUtils.hexToBytes(toSend); | ||
34 | - int ri = alex_crc16(algorithm,bb); | ||
35 | - if(!algorithm.getByteOrder()){ | ||
36 | - ri=ByteUtils.revert(ri); | ||
37 | - } | ||
38 | - byte[] crc = ByteUtils.getBytes((short)ri); | ||
39 | - return ByteUtils.bytesToHex(crc); | ||
40 | - } | 6 | + /** |
7 | + * crc16 for modbus | ||
8 | + * | ||
9 | + * @param buf buffer to be crc | ||
10 | + * @return crc result word | ||
11 | + */ | ||
12 | + static int alex_crc16(TkModBusCheckType algorithm, byte[] buf) { | ||
13 | + int i, j; | ||
14 | + int c, crc = algorithm.getInit(); | ||
15 | + for (i = 0; i < buf.length; i++) { | ||
16 | + c = buf[i] & 0x00FF; // 原始数据高8位和初始值进行异或运算保持不变 | ||
17 | + crc ^= c; | ||
18 | + for (j = 0; j < 8; j++) { // 原始数据左移8位 | ||
19 | + if ((crc & 0x0001) != 0) { | ||
20 | + crc >>= 1; | ||
21 | + crc ^= algorithm.getXorIn(); // 把处理之后的数据和多项式进行模2除法,求得余数 | ||
22 | + } else crc >>= 1; | ||
23 | + } | ||
24 | + } | ||
25 | + return (crc); | ||
26 | + } | ||
41 | 27 | ||
28 | + /** | ||
29 | + * convert string to buffer and append the crc check word to the end of buffer | ||
30 | + * | ||
31 | + * @param toSend string to be convert | ||
32 | + * @return buffer with crc word, high byte if after low byte according the modbus | ||
33 | + */ | ||
34 | + public static String getSendBuf(TkModBusCheckType algorithm, String toSend) { | ||
35 | + byte[] bb = ByteUtils.hexToBytes(toSend); | ||
36 | + int ri = alex_crc16(algorithm, bb); | ||
37 | + if (!algorithm.getByteOrder()) { | ||
38 | + ri = ByteUtils.revert(ri); | ||
39 | + } | ||
40 | + byte[] crc = ByteUtils.getBytes((short) ri); | ||
41 | + return ByteUtils.bytesToHex(crc); | ||
42 | + } | ||
42 | } | 43 | } |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/utils/HexConvertUtils.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.utils; | ||
2 | + | ||
3 | +import org.thingsboard.server.common.data.yunteng.enums.AttributeSourceDataTypeEnum; | ||
4 | +import org.thingsboard.server.common.data.yunteng.enums.HexByteOrderEnum; | ||
5 | +import java.nio.ByteOrder; | ||
6 | +import java.text.DecimalFormat; | ||
7 | + | ||
8 | +public class HexConvertUtils { | ||
9 | + /** | ||
10 | + * 16位有符号、无符号HEX转为short | ||
11 | + * 顺序为AB | ||
12 | + * | ||
13 | + * @param hexStr 16进制HEX,长度4 | ||
14 | + * @return short值 | ||
15 | + */ | ||
16 | + public static short int16OrUint16ToShort(String hexStr) { | ||
17 | + hexStr = replaceHexStr(hexStr); | ||
18 | + // 先转换为无符号16位整数(int类型) | ||
19 | + int unsignedValue = Integer.parseInt(hexStr, 16); | ||
20 | + // 对于负数,通过补码计算还原实际的负数值 | ||
21 | + short signedValue; | ||
22 | + if ((unsignedValue & 0x8000) != 0) { | ||
23 | + // 最高位为1,则该数值是一个负数 | ||
24 | + signedValue = (short) (unsignedValue - 0x10000); | ||
25 | + } else { | ||
26 | + // 最高位为0,则该数值是一个正数,可以直接转换为short类型 | ||
27 | + signedValue = (short) unsignedValue; | ||
28 | + } | ||
29 | + return signedValue; | ||
30 | + } | ||
31 | + | ||
32 | + /** | ||
33 | + * 32位有符号、无符号HEX转long | ||
34 | + * 顺序位AB_CD | ||
35 | + * @param hexValue 16进制HEX,长度8 | ||
36 | + * @return long值 | ||
37 | + */ | ||
38 | + public static long int32OrUint32ToLong(String hexValue){ | ||
39 | + hexValue = replaceHexStr(hexValue); | ||
40 | + long unsignedIntValue = Integer.parseUnsignedInt(hexValue, 16); | ||
41 | + // 计算实际的有符号32位整数值 | ||
42 | + long signedIntValue; | ||
43 | + if ((unsignedIntValue & 0x80000000) == 0) { | ||
44 | + // 如果最高位是0,则直接转换为正数 | ||
45 | + signedIntValue = unsignedIntValue; | ||
46 | + } else { | ||
47 | + // 如果最高位是1,则需要取反加1得到原码,然后再转换为负数 | ||
48 | + signedIntValue = -((unsignedIntValue ^ 0xFFFFFFFF) + 1); | ||
49 | + } | ||
50 | + return signedIntValue; | ||
51 | + } | ||
52 | + | ||
53 | + /** | ||
54 | + * 将16进制转位2进制倒序,并返回int数组 用于寄存器的位计算 | ||
55 | + * @param hex 16进制hex | ||
56 | + * @param binaryLength 二进制长度 | ||
57 | + * @return int数组 | ||
58 | + */ | ||
59 | + public static int[] hexToBinaryPaddedAndReversed(String hex, int binaryLength) { | ||
60 | + // 将16进制字符串转换为整数 | ||
61 | + int number = Integer.parseInt(hex, 16); | ||
62 | + | ||
63 | + // 将整数转换为二进制字符串 | ||
64 | + String binaryString = Integer.toBinaryString(number); | ||
65 | + | ||
66 | + // 检查是否需要补位 | ||
67 | + while (binaryString.length() < binaryLength) { | ||
68 | + binaryString = "0" + binaryString; | ||
69 | + } | ||
70 | + | ||
71 | + // 对二进制字符串进行反转 | ||
72 | + char[] chars = binaryString.toCharArray(); | ||
73 | + StringBuilder reversedBinaryString = new StringBuilder(); | ||
74 | + for (int i = chars.length - 1; i >= 0; i--) { | ||
75 | + reversedBinaryString.append(chars[i]); | ||
76 | + } | ||
77 | + int[] result = new int[binaryLength]; | ||
78 | + for(int i = 0; i < reversedBinaryString.length(); i++) { | ||
79 | + result[i] = Integer.valueOf(reversedBinaryString.substring(i,i+1)); | ||
80 | + } | ||
81 | + return result; | ||
82 | + } | ||
83 | + | ||
84 | + /** | ||
85 | + * 把数据的HEX位置调整好 | ||
86 | + * | ||
87 | + * @param hex 原始hex | ||
88 | + * @param dataTypeEnum 属性类型 | ||
89 | + * @return 排序后的hex | ||
90 | + */ | ||
91 | + public static String convertHexOrder(String hex, AttributeSourceDataTypeEnum dataTypeEnum) { | ||
92 | + HexByteOrderEnum hexByteOrderEnum; | ||
93 | + switch (dataTypeEnum) { | ||
94 | + case FLOAT_AB_CD: | ||
95 | + case FLOAT_BA_DC: | ||
96 | + case FLOAT_CD_AB: | ||
97 | + case FLOAT_DC_BA: | ||
98 | + hexByteOrderEnum = HexByteOrderEnum.valueOf(dataTypeEnum.name().split("FLOAT_")[1]); | ||
99 | + break; | ||
100 | + case INT16_AB: | ||
101 | + case UINT16_AB: | ||
102 | + case INT16_BA: | ||
103 | + case UINT16_BA: | ||
104 | + hexByteOrderEnum = HexByteOrderEnum.valueOf(dataTypeEnum.name().split("_")[1]); | ||
105 | + break; | ||
106 | + case INT32_AB_CD: | ||
107 | + case UINT32_AB_CD: | ||
108 | + case INT32_BA_DC: | ||
109 | + case UINT32_BA_DC: | ||
110 | + case INT32_CD_AB: | ||
111 | + case UINT32_CD_AB: | ||
112 | + case INT32_DC_BA: | ||
113 | + case UINT32_DC_BA: | ||
114 | + hexByteOrderEnum = HexByteOrderEnum.valueOf(dataTypeEnum.name().split("32_")[1]); | ||
115 | + break; | ||
116 | + default: | ||
117 | + hexByteOrderEnum = HexByteOrderEnum.AB; | ||
118 | + } | ||
119 | + return convertHex(hex, hexByteOrderEnum); | ||
120 | + } | ||
121 | + | ||
122 | + public static String convertHex(String hex, HexByteOrderEnum hexByteOrderEnum) { | ||
123 | + String result; | ||
124 | + switch (hexByteOrderEnum) { | ||
125 | + case AB: | ||
126 | + result = processHexH(hex); | ||
127 | + break; | ||
128 | + case BA: | ||
129 | + result = processHexL(hex); | ||
130 | + break; | ||
131 | + case BA_DC: | ||
132 | + result = processHexL(hex.substring(0, 4)) + processHexL(hex.substring(4, 8)); | ||
133 | + break; | ||
134 | + case CD_AB: | ||
135 | + result = processHexH(hex.substring(4, 8)) + processHexH(hex.substring(0, 4)); | ||
136 | + break; | ||
137 | + case DC_BA: | ||
138 | + result = processHexL(hex.substring(4, 8)) + processHexL(hex.substring(0, 4)); | ||
139 | + break; | ||
140 | + case AB_CD: | ||
141 | + result = processHexH(hex.substring(0, 4)) + processHexH(hex.substring(4, 8)); | ||
142 | + break; | ||
143 | + default: | ||
144 | + result = ""; | ||
145 | + } | ||
146 | + return result; | ||
147 | + } | ||
148 | + | ||
149 | + private static String processHexL(String hex) { | ||
150 | + String l = hex.substring(0, 2); | ||
151 | + String h = hex.substring(2, 4); | ||
152 | + return h + l; | ||
153 | + } | ||
154 | + | ||
155 | + private static String processHexH(String hex) { | ||
156 | + String h = hex.substring(0, 2); | ||
157 | + String l = hex.substring(2, 4); | ||
158 | + return h + l; | ||
159 | + } | ||
160 | + | ||
161 | + /** | ||
162 | + * 一个字节hex转为byte值 | ||
163 | + * | ||
164 | + * @param hex 16进制hex,长度2 | ||
165 | + * @return byte值 | ||
166 | + */ | ||
167 | + private static byte hexToByte(String hex) { | ||
168 | + hex = replaceHexStr(hex); | ||
169 | + return (byte) Integer.parseInt(hex, 16); | ||
170 | + } | ||
171 | + | ||
172 | + /** | ||
173 | + * 16进制转为位进行计算 | ||
174 | + * | ||
175 | + * @param hex 16进制HEX,长度为2 | ||
176 | + * @return int数组 | ||
177 | + */ | ||
178 | + public static int[] hexToBits(String hex) { | ||
179 | + byte value = hexToByte(hex); | ||
180 | + int[] bits = new int[8]; | ||
181 | + for (int i = 0; i < 8; i++) { | ||
182 | + bits[i] = (value >> i) & 1; | ||
183 | + } | ||
184 | + return bits; | ||
185 | + } | ||
186 | + | ||
187 | + /** | ||
188 | + * 16进制转为boolean进行计算 | ||
189 | + * | ||
190 | + * @param hex hex 16进制HEX,长度为2 | ||
191 | + * @return boolean数组 | ||
192 | + */ | ||
193 | + public static boolean[] hexToBooleans(String hex) { | ||
194 | + | ||
195 | + byte value = hexToByte(hex); | ||
196 | + boolean[] booleans = new boolean[8]; | ||
197 | + for (int i = 0; i < 8; i++) { | ||
198 | + booleans[i] = (value & (1 << i)) != 0; | ||
199 | + } | ||
200 | + return booleans; | ||
201 | + } | ||
202 | + | ||
203 | + /** | ||
204 | + * 16进制转为Ascii码 | ||
205 | + * | ||
206 | + * @param hex 16进制 | ||
207 | + * @return ascii字符串 | ||
208 | + */ | ||
209 | + public static String hexToASCII(String hex) { | ||
210 | + hex = replaceHexStr(hex); | ||
211 | + StringBuilder asciiString = new StringBuilder(); | ||
212 | + for (int i = 0; i < hex.length(); i += 2) { | ||
213 | + String byteValueStr = hex.substring(i, i + 2); | ||
214 | + int byteValue = Integer.parseInt(byteValueStr, 16); // 将HEX转为十进制 | ||
215 | + | ||
216 | + // 对于ASCII范围内的值(0-127),直接转换 | ||
217 | + if (byteValue >= 0 && byteValue <= 127) { | ||
218 | + asciiString.append((char) byteValue); | ||
219 | + } | ||
220 | + // 对于超过ASCII范围的值,我们将其转换为16进制字符串表示 | ||
221 | + else { | ||
222 | + asciiString.append("0x").append(byteValueStr); | ||
223 | + } | ||
224 | + } | ||
225 | + return asciiString.toString(); | ||
226 | + } | ||
227 | + | ||
228 | + public static Object setDeviceTelemetry( | ||
229 | + String hex, | ||
230 | + AttributeSourceDataTypeEnum dataTypeEnum, | ||
231 | + int bitAddress, | ||
232 | + float scaleFactor) { | ||
233 | + Object value = new Object(); | ||
234 | + DecimalFormat intFormat = new DecimalFormat("#.00"); | ||
235 | + switch (dataTypeEnum) { | ||
236 | + case FLOAT_AB_CD: | ||
237 | + case FLOAT_BA_DC: | ||
238 | + case FLOAT_CD_AB: | ||
239 | + case FLOAT_DC_BA: | ||
240 | + hex = convertHexOrder(hex, dataTypeEnum); | ||
241 | + value = | ||
242 | + intFormat.format( | ||
243 | + IEEE754.parseSinglePrecisionHex(hex, ByteOrder.BIG_ENDIAN) * scaleFactor); | ||
244 | + break; | ||
245 | + case DOUBLE: | ||
246 | + value = | ||
247 | + intFormat.format( | ||
248 | + IEEE754.parseDoublePrecisionHex(hex, ByteOrder.BIG_ENDIAN) * scaleFactor); | ||
249 | + break; | ||
250 | + case INT16_AB: | ||
251 | + case UINT16_AB: | ||
252 | + case INT16_BA: | ||
253 | + case UINT16_BA: | ||
254 | + hex = convertHexOrder(hex, dataTypeEnum); | ||
255 | + value = intFormat.format(int16OrUint16ToShort(hex) * scaleFactor); | ||
256 | + break; | ||
257 | + case INT32_AB_CD: | ||
258 | + case UINT32_AB_CD: | ||
259 | + case INT32_BA_DC: | ||
260 | + case UINT32_BA_DC: | ||
261 | + case INT32_CD_AB: | ||
262 | + case UINT32_CD_AB: | ||
263 | + case INT32_DC_BA: | ||
264 | + case UINT32_DC_BA: | ||
265 | + hex = convertHexOrder(hex, dataTypeEnum); | ||
266 | + value = intFormat.format(int32OrUint32ToLong(hex) * scaleFactor); | ||
267 | + break; | ||
268 | + case STRING: | ||
269 | + value = hexToASCII(hex); | ||
270 | + break; | ||
271 | + case BITS: | ||
272 | + value = hexToBinaryPaddedAndReversed(hex,hex.length()/2*8)[bitAddress]; | ||
273 | + break; | ||
274 | + case BOOLEAN: | ||
275 | + value = hexToBooleans(hex)[bitAddress]; | ||
276 | + break; | ||
277 | + } | ||
278 | + | ||
279 | + return value; | ||
280 | + } | ||
281 | + | ||
282 | + private static String replaceHexStr(String hex) { | ||
283 | + return hex.trim().replaceAll(" ", ""); | ||
284 | + } | ||
285 | + | ||
286 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.utils; | ||
2 | + | ||
3 | +import java.nio.ByteBuffer; | ||
4 | +import java.nio.ByteOrder; | ||
5 | + | ||
6 | +public class IEEE754 { | ||
7 | + /** | ||
8 | + * 定义方法用于解析单精度浮点数 | ||
9 | + * | ||
10 | + * @param hex 32位单精度HEX | ||
11 | + * @param byteOrder 字节序 | ||
12 | + * @return 单精度浮点数 | ||
13 | + */ | ||
14 | + public static float parseSinglePrecisionHex(String hex, ByteOrder byteOrder) { | ||
15 | + byte[] bytes = ByteUtils.hexStr2Bytes(hex); | ||
16 | + | ||
17 | + if (bytes.length != 4) { | ||
18 | + throw new IllegalArgumentException( | ||
19 | + "Invalid length for single precision float, expected 4 bytes"); | ||
20 | + } | ||
21 | + | ||
22 | + return ByteBuffer.wrap(bytes).order(byteOrder).getFloat(); | ||
23 | + } | ||
24 | + | ||
25 | + /** | ||
26 | + * 定义方法用于解析双精度浮点数 | ||
27 | + * | ||
28 | + * @param hex 64位双精度HEX | ||
29 | + * @param byteOrder 字节序 | ||
30 | + * @return 双精度浮点数 | ||
31 | + */ | ||
32 | + public static double parseDoublePrecisionHex(String hex, ByteOrder byteOrder) { | ||
33 | + byte[] bytes = ByteUtils.hexStr2Bytes(hex); | ||
34 | + | ||
35 | + if (bytes.length != 8) { | ||
36 | + throw new IllegalArgumentException( | ||
37 | + "Invalid length for double precision float, expected 8 bytes"); | ||
38 | + } | ||
39 | + | ||
40 | + return ByteBuffer.wrap(bytes).order(byteOrder).getDouble(); | ||
41 | + } | ||
42 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/utils/ModbusUtils.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.utils; | ||
2 | + | ||
3 | +import org.thingsboard.server.common.data.StringUtils; | ||
4 | +import org.thingsboard.server.common.data.yunteng.enums.AttributeSourceDataTypeEnum; | ||
5 | +import org.thingsboard.server.common.data.yunteng.enums.TkModBusCheckType; | ||
6 | +import java.util.ArrayList; | ||
7 | +import java.util.List; | ||
8 | + | ||
9 | +public class ModbusUtils { | ||
10 | + /** | ||
11 | + * 校验上报的HEX是否为标准Modbus格式 标准的Modbus帧包括:设备地址、功能码、数据和校验码 | ||
12 | + * | ||
13 | + * @param hexString 上报的HEX | ||
14 | + * @return true满足 false不满足 | ||
15 | + */ | ||
16 | + public static boolean isValidModbusResponseFrame(String hexString) { | ||
17 | + if (StringUtils.isEmpty(hexString)) { | ||
18 | + return false; | ||
19 | + } | ||
20 | + // 去除可能的空格 | ||
21 | + hexString = hexString.trim().replaceAll(" ", ""); | ||
22 | + | ||
23 | + // 判断长度是否为偶数且至少为6(我们只考虑ASCII帧) | ||
24 | + if (hexString.length() % 2 != 0 || hexString.length() < 6) { | ||
25 | + return false; | ||
26 | + } | ||
27 | + | ||
28 | + // 获取从机地址、功能码和数据 | ||
29 | + String slaveAddress = hexString.substring(0, 2); | ||
30 | + String functionCode = hexString.substring(2, 4); | ||
31 | + String data = hexString.substring(4, hexString.length() - 4); | ||
32 | + | ||
33 | + // 计算校验码 | ||
34 | + String calculatedCRC = | ||
35 | + CrcUtils.getSendBuf(TkModBusCheckType.CRC_16_LOWER, slaveAddress + functionCode + data); | ||
36 | + | ||
37 | + // 获取并验证校验码 | ||
38 | + String crc = hexString.substring(hexString.length() - 4); | ||
39 | + if (!calculatedCRC.equals(crc)) { | ||
40 | + return false; | ||
41 | + } | ||
42 | + | ||
43 | + // 检查功能码是否有效 | ||
44 | + if (!isValidFunctionCode(functionCode)) { | ||
45 | + return false; | ||
46 | + } | ||
47 | + return isValidData(data); | ||
48 | + } | ||
49 | + | ||
50 | + private static boolean isValidFunctionCode(String functionCode) { | ||
51 | + switch (functionCode) { | ||
52 | + // 读取线圈状态 | ||
53 | + case "01": | ||
54 | + return true; | ||
55 | + // 读取输入状态 | ||
56 | + case "02": | ||
57 | + return true; | ||
58 | + // 读取保持寄存器 | ||
59 | + case "03": | ||
60 | + return true; | ||
61 | + // 读取输入寄存器 | ||
62 | + case "04": | ||
63 | + return true; | ||
64 | + default: | ||
65 | + return false; | ||
66 | + } | ||
67 | + } | ||
68 | + | ||
69 | + /** | ||
70 | + * 截取字符串 | ||
71 | + * | ||
72 | + * @param str 原始字符串 | ||
73 | + * @param length 截取长度 | ||
74 | + * @return 截取后的有序列表 | ||
75 | + */ | ||
76 | + public static List<String> splitString(String str, int length) { | ||
77 | + List<String> result = new ArrayList<>(); | ||
78 | + for (int i = 0; i < str.length(); i += length) { | ||
79 | + if (i + length <= str.length()) { | ||
80 | + result.add(str.substring(i, i + length)); | ||
81 | + } else { | ||
82 | + result.add(str.substring(i)); | ||
83 | + } | ||
84 | + } | ||
85 | + return result; | ||
86 | + } | ||
87 | + | ||
88 | + /** | ||
89 | + * 根据原始数据类型,确认HEX的截取长度 | ||
90 | + * | ||
91 | + * @param dataTypeEnum 原始数据类型 | ||
92 | + * @return 截取长度 | ||
93 | + */ | ||
94 | + public static int getDataLengthBySourceDataType(AttributeSourceDataTypeEnum dataTypeEnum) { | ||
95 | + int length; | ||
96 | + switch (dataTypeEnum) { | ||
97 | + case UINT16_AB: | ||
98 | + case UINT16_BA: | ||
99 | + case INT16_AB: | ||
100 | + case INT16_BA: | ||
101 | + length = 4; | ||
102 | + break; | ||
103 | + case UINT32_AB_CD: | ||
104 | + case UINT32_DC_BA: | ||
105 | + case UINT32_BA_DC: | ||
106 | + case UINT32_CD_AB: | ||
107 | + case INT32_AB_CD: | ||
108 | + case INT32_DC_BA: | ||
109 | + case INT32_BA_DC: | ||
110 | + case INT32_CD_AB: | ||
111 | + case FLOAT_AB_CD: | ||
112 | + case FLOAT_BA_DC: | ||
113 | + case FLOAT_CD_AB: | ||
114 | + case FLOAT_DC_BA: | ||
115 | + length = 8; | ||
116 | + break; | ||
117 | + case DOUBLE: | ||
118 | + length = 16; | ||
119 | + break; | ||
120 | + default: | ||
121 | + length = 2; | ||
122 | + } | ||
123 | + return length; | ||
124 | + } | ||
125 | + | ||
126 | + private static boolean isValidData(String data) { | ||
127 | + // 寄存器字节数长度 | ||
128 | + int dataLength = Integer.parseInt(data.substring(0, 2), 16); | ||
129 | + // 实际数据长度 | ||
130 | + int realDataLength = data.substring(2, data.length()).length() / 2; | ||
131 | + return dataLength == realDataLength; | ||
132 | + } | ||
133 | +} |
@@ -23,6 +23,8 @@ import static org.thingsboard.server.common.transport.service.DefaultTransportSe | @@ -23,6 +23,8 @@ import static org.thingsboard.server.common.transport.service.DefaultTransportSe | ||
23 | import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_OPEN; | 23 | import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_OPEN; |
24 | 24 | ||
25 | import com.fasterxml.jackson.databind.JsonNode; | 25 | import com.fasterxml.jackson.databind.JsonNode; |
26 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
27 | +import com.fasterxml.jackson.databind.node.TextNode; | ||
26 | import com.google.common.util.concurrent.FutureCallback; | 28 | import com.google.common.util.concurrent.FutureCallback; |
27 | import com.google.common.util.concurrent.Futures; | 29 | import com.google.common.util.concurrent.Futures; |
28 | import com.google.common.util.concurrent.ListenableFuture; | 30 | import com.google.common.util.concurrent.ListenableFuture; |
@@ -34,23 +36,17 @@ import io.netty.channel.ChannelHandlerContext; | @@ -34,23 +36,17 @@ import io.netty.channel.ChannelHandlerContext; | ||
34 | import io.netty.channel.ChannelInboundHandlerAdapter; | 36 | import io.netty.channel.ChannelInboundHandlerAdapter; |
35 | import io.netty.handler.codec.mqtt.*; | 37 | import io.netty.handler.codec.mqtt.*; |
36 | import io.netty.handler.ssl.SslHandler; | 38 | import io.netty.handler.ssl.SslHandler; |
37 | -import io.netty.util.CharsetUtil; | ||
38 | import io.netty.util.ReferenceCountUtil; | 39 | import io.netty.util.ReferenceCountUtil; |
39 | import io.netty.util.concurrent.Future; | 40 | import io.netty.util.concurrent.Future; |
40 | import io.netty.util.concurrent.GenericFutureListener; | 41 | import io.netty.util.concurrent.GenericFutureListener; |
41 | import java.io.IOException; | 42 | import java.io.IOException; |
42 | import java.net.InetSocketAddress; | 43 | import java.net.InetSocketAddress; |
43 | -import java.util.List; | ||
44 | -import java.util.Map; | ||
45 | -import java.util.Optional; | ||
46 | -import java.util.UUID; | 44 | +import java.util.*; |
47 | import java.util.concurrent.atomic.AtomicInteger; | 45 | import java.util.concurrent.atomic.AtomicInteger; |
48 | -import java.util.regex.Pattern; | 46 | + |
49 | import lombok.extern.slf4j.Slf4j; | 47 | import lombok.extern.slf4j.Slf4j; |
50 | import org.apache.commons.lang3.StringUtils; | 48 | import org.apache.commons.lang3.StringUtils; |
51 | import org.checkerframework.checker.nullness.qual.Nullable; | 49 | import org.checkerframework.checker.nullness.qual.Nullable; |
52 | -import org.springframework.boot.context.event.ApplicationReadyEvent; | ||
53 | -import org.springframework.context.event.EventListener; | ||
54 | import org.thingsboard.common.util.JacksonUtil; | 50 | import org.thingsboard.common.util.JacksonUtil; |
55 | import org.thingsboard.server.common.data.DataConstants; | 51 | import org.thingsboard.server.common.data.DataConstants; |
56 | import org.thingsboard.server.common.data.Device; | 52 | import org.thingsboard.server.common.data.Device; |
@@ -59,9 +55,11 @@ import org.thingsboard.server.common.data.DeviceTransportType; | @@ -59,9 +55,11 @@ import org.thingsboard.server.common.data.DeviceTransportType; | ||
59 | import org.thingsboard.server.common.data.device.profile.TkTcpDeviceProfileTransportConfiguration; | 55 | import org.thingsboard.server.common.data.device.profile.TkTcpDeviceProfileTransportConfiguration; |
60 | import org.thingsboard.server.common.data.id.DeviceId; | 56 | import org.thingsboard.server.common.data.id.DeviceId; |
61 | import org.thingsboard.server.common.data.rpc.RpcStatus; | 57 | import org.thingsboard.server.common.data.rpc.RpcStatus; |
62 | -import org.thingsboard.server.common.data.yunteng.enums.StatusEnum; | ||
63 | -import org.thingsboard.server.common.data.yunteng.enums.TkScriptFunctionType; | ||
64 | -import org.thingsboard.server.common.data.yunteng.utils.ByteUtils; | 58 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; |
59 | +import org.thingsboard.server.common.data.yunteng.dto.TkThingsModel; | ||
60 | +import org.thingsboard.server.common.data.yunteng.enums.AttributeSourceDataTypeEnum; | ||
61 | +import org.thingsboard.server.common.data.yunteng.enums.ProtocolAnalysisEnum; | ||
62 | +import org.thingsboard.server.common.data.yunteng.utils.*; | ||
65 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; | 63 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; |
66 | import org.thingsboard.server.common.transport.SessionMsgListener; | 64 | import org.thingsboard.server.common.transport.SessionMsgListener; |
67 | import org.thingsboard.server.common.transport.TransportService; | 65 | import org.thingsboard.server.common.transport.TransportService; |
@@ -199,15 +197,72 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -199,15 +197,72 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
199 | if (!checkConnected(ctx, tcpMessage)) { | 197 | if (!checkConnected(ctx, tcpMessage)) { |
200 | return; | 198 | return; |
201 | } | 199 | } |
200 | + //判断协议类型 | ||
201 | + TkTcpDeviceProfileTransportConfiguration transportConfiguration = | ||
202 | + (TkTcpDeviceProfileTransportConfiguration) deviceSessionCtx.getDeviceProfile().getProfileData().getTransportConfiguration(); | ||
203 | + switch (transportConfiguration.getProtocol()) | ||
204 | + { | ||
205 | + case CUSTOM: | ||
206 | + customScriptProcess(ctx,tcpMessage); | ||
207 | + break; | ||
208 | + case MODBUS_RTU: | ||
209 | + modbusRtuProcess(ctx,tcpMessage); | ||
210 | + break; | ||
211 | + | ||
212 | + } | ||
213 | + } | ||
214 | + | ||
215 | + | ||
216 | + private void modbusRtuProcess(ChannelHandlerContext ctx, String tcpMessage){ | ||
217 | + //移除空格 | ||
218 | + String hexString = tcpMessage.trim().replaceAll(" ",""); | ||
219 | + //判断是否为16进制HEX | ||
220 | + if(hexString.matches("-?[0-9a-fA-F]+")){ | ||
221 | + boolean modbusCheckResult = ModbusUtils.isValidModbusResponseFrame(hexString); | ||
222 | + //判断是否满足modbus标准,满足的才处理不满足的过滤掉 | ||
223 | + if(modbusCheckResult){ | ||
224 | + //根据上报的地址码,判断该条消息归属于那个设备的数据 | ||
225 | + String deviceAddress = hexString.substring(0,2); | ||
226 | + int deviceCode = Integer.parseInt(deviceSessionCtx.getDeviceCode(),16); | ||
227 | + int deviceAddressCode = Integer.parseInt(deviceAddress,16); | ||
228 | + if(gatewaySessionHandler != null && deviceCode != deviceAddressCode){ | ||
229 | + gatewaySessionHandler.onDeviceTelemetry(deviceAddress, null, hexString, | ||
230 | + ProtocolAnalysisEnum.MODBUS_RTU); | ||
231 | + }else{ | ||
232 | + if(deviceCode == deviceAddressCode){ | ||
233 | + processCustomDirectDeviceMsg(ctx, deviceSessionCtx.getPayloadAdaptor() | ||
234 | + .convertModbusHexToPublish(deviceSessionCtx,hexString).get()); | ||
235 | + } | ||
236 | + } | ||
237 | + } | ||
238 | + } | ||
239 | + } | ||
240 | + | ||
241 | + | ||
242 | + | ||
243 | + | ||
244 | + private void customScriptProcess(ChannelHandlerContext ctx, String tcpMessage){ | ||
202 | deviceSessionCtx.doUpScript(tcpMessage, r -> { | 245 | deviceSessionCtx.doUpScript(tcpMessage, r -> { |
203 | - if (gatewaySessionHandler != null) { | ||
204 | - processGatewayDeviceMsg(ctx, r); | 246 | + //根据网关上报的消息,判断消息的来源是否为网关子设备,判断依据deviceCode即设备地址码或设备标识符 |
247 | + if (gatewaySessionHandler != null && checkMessageIsFromSensor(r.getDatas())) { | ||
248 | + processCustomGatewayDeviceMsg(ctx, r); | ||
249 | + }else{ | ||
250 | + processCustomDirectDeviceMsg(ctx, r); | ||
205 | } | 251 | } |
206 | - processDirectDeviceMsg(ctx, r); | ||
207 | }); | 252 | }); |
208 | - | ||
209 | } | 253 | } |
210 | 254 | ||
255 | + private boolean checkMessageIsFromSensor(Map<String,Object> dataMap){ | ||
256 | + boolean isSensorMessage = true; | ||
257 | + String gateWayDeviceCode = deviceSessionCtx.getDeviceCode(); | ||
258 | + for (Map.Entry<String,Object> entry :dataMap.entrySet()){ | ||
259 | + if(entry.getKey().equals(gateWayDeviceCode)){ | ||
260 | + isSensorMessage = false; | ||
261 | + break; | ||
262 | + } | ||
263 | + } | ||
264 | + return isSensorMessage; | ||
265 | + } | ||
211 | 266 | ||
212 | /** | 267 | /** |
213 | * 上行脚本解析结果是否包含数据 | 268 | * 上行脚本解析结果是否包含数据 |
@@ -221,8 +276,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -221,8 +276,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
221 | return true; | 276 | return true; |
222 | } | 277 | } |
223 | 278 | ||
224 | - | ||
225 | - private void processGatewayDeviceMsg(ChannelHandlerContext ctx, TcpUpEntry tcpMessage) { | 279 | + private void processCustomGatewayDeviceMsg(ChannelHandlerContext ctx, TcpUpEntry tcpMessage) { |
226 | log.trace("[{}][{}] Processing publish msg [{}]!", sessionId, deviceSessionCtx.getDeviceId(), tcpMessage); | 280 | log.trace("[{}][{}] Processing publish msg [{}]!", sessionId, deviceSessionCtx.getDeviceId(), tcpMessage); |
227 | try { | 281 | try { |
228 | Map<String, Object> datas = tcpMessage.getDatas(); | 282 | Map<String, Object> datas = tcpMessage.getDatas(); |
@@ -232,7 +286,8 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -232,7 +286,8 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
232 | return; | 286 | return; |
233 | } | 287 | } |
234 | if (tcpMessage.getTelemetry()) { | 288 | if (tcpMessage.getTelemetry()) { |
235 | - gatewaySessionHandler.onDeviceTelemetry(devName, tcpMessage.getRequestId(), param.toString()); | 289 | + gatewaySessionHandler.onDeviceTelemetry(devName, tcpMessage.getRequestId(), param.toString(), |
290 | + ProtocolAnalysisEnum.CUSTOM); | ||
236 | } else { | 291 | } else { |
237 | // gatewaySessionHandler.onDeviceRpcResponse(devName, tcpMessage.getRequestId(), param.toString()); | 292 | // gatewaySessionHandler.onDeviceRpcResponse(devName, tcpMessage.getRequestId(), param.toString()); |
238 | } | 293 | } |
@@ -248,7 +303,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -248,7 +303,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
248 | } | 303 | } |
249 | } | 304 | } |
250 | 305 | ||
251 | - private void processDirectDeviceMsg(ChannelHandlerContext ctx, TcpUpEntry tcpMessage) { | 306 | + private void processCustomDirectDeviceMsg(ChannelHandlerContext ctx, TcpUpEntry tcpMessage) { |
252 | log.trace("[{}][{}] Processing publish msg [{}]!", sessionId, deviceSessionCtx.getDeviceId(), tcpMessage); | 307 | log.trace("[{}][{}] Processing publish msg [{}]!", sessionId, deviceSessionCtx.getDeviceId(), tcpMessage); |
253 | try { | 308 | try { |
254 | TcpTransportAdaptor payloadAdaptor = deviceSessionCtx.getPayloadAdaptor(); | 309 | TcpTransportAdaptor payloadAdaptor = deviceSessionCtx.getPayloadAdaptor(); |
@@ -435,7 +490,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -435,7 +490,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
435 | } else { | 490 | } else { |
436 | DeviceProfile profile = msg.getDeviceProfile(); | 491 | DeviceProfile profile = msg.getDeviceProfile(); |
437 | TkTcpDeviceProfileTransportConfiguration tcpConfig = (TkTcpDeviceProfileTransportConfiguration) profile.getProfileData().getTransportConfiguration(); | 492 | TkTcpDeviceProfileTransportConfiguration tcpConfig = (TkTcpDeviceProfileTransportConfiguration) profile.getProfileData().getTransportConfiguration(); |
438 | - if (scriptId != null && !tcpConfig.getAuthScriptId().equals(scriptId.toString())) { | 493 | + if (scriptId != null&& tcpConfig.getProtocol().equals(ProtocolAnalysisEnum.CUSTOM) && !tcpConfig.getAuthScriptId().equals(scriptId.toString())) { |
439 | authedCounter.incrementAndGet(); | 494 | authedCounter.incrementAndGet(); |
440 | return; | 495 | return; |
441 | } | 496 | } |
@@ -562,6 +617,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -562,6 +617,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
562 | @Override | 617 | @Override |
563 | public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) { | 618 | public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) { |
564 | deviceSessionCtx.onDeviceUpdate(sessionInfo, device, deviceProfileOpt); | 619 | deviceSessionCtx.onDeviceUpdate(sessionInfo, device, deviceProfileOpt); |
620 | + deviceSessionCtx.setDeviceCode(JacksonUtil.toString(device.getAdditionalInfo())); | ||
565 | } | 621 | } |
566 | 622 | ||
567 | @Override | 623 | @Override |
common/transport/tcp/src/main/java/org/thingsboard/server/transport/tcp/adaptors/JsonTcpAdaptor.java
@@ -15,9 +15,9 @@ | @@ -15,9 +15,9 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.transport.tcp.adaptors; | 16 | package org.thingsboard.server.transport.tcp.adaptors; |
17 | 17 | ||
18 | -import com.google.common.util.concurrent.Futures; | ||
19 | -import com.google.common.util.concurrent.ListenableFuture; | ||
20 | -import com.google.common.util.concurrent.MoreExecutors; | 18 | +import com.fasterxml.jackson.databind.JsonNode; |
19 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
20 | +import com.fasterxml.jackson.databind.node.TextNode; | ||
21 | import com.google.gson.JsonElement; | 21 | import com.google.gson.JsonElement; |
22 | import com.google.gson.JsonObject; | 22 | import com.google.gson.JsonObject; |
23 | import com.google.gson.JsonParser; | 23 | import com.google.gson.JsonParser; |
@@ -29,6 +29,11 @@ import org.springframework.stereotype.Component; | @@ -29,6 +29,11 @@ import org.springframework.stereotype.Component; | ||
29 | import org.springframework.util.StringUtils; | 29 | import org.springframework.util.StringUtils; |
30 | import org.thingsboard.common.util.JacksonUtil; | 30 | import org.thingsboard.common.util.JacksonUtil; |
31 | import org.thingsboard.server.common.data.ota.OtaPackageType; | 31 | import org.thingsboard.server.common.data.ota.OtaPackageType; |
32 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
33 | +import org.thingsboard.server.common.data.yunteng.dto.TkThingsModel; | ||
34 | +import org.thingsboard.server.common.data.yunteng.enums.AttributeSourceDataTypeEnum; | ||
35 | +import org.thingsboard.server.common.data.yunteng.utils.HexConvertUtils; | ||
36 | +import org.thingsboard.server.common.data.yunteng.utils.ModbusUtils; | ||
32 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 37 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
33 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 38 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
34 | import org.thingsboard.server.gen.transport.TransportProtos; | 39 | import org.thingsboard.server.gen.transport.TransportProtos; |
@@ -38,6 +43,8 @@ import java.nio.charset.Charset; | @@ -38,6 +43,8 @@ import java.nio.charset.Charset; | ||
38 | import java.nio.charset.StandardCharsets; | 43 | import java.nio.charset.StandardCharsets; |
39 | import java.util.*; | 44 | import java.util.*; |
40 | import java.util.concurrent.ExecutionException; | 45 | import java.util.concurrent.ExecutionException; |
46 | +import java.util.stream.Collectors; | ||
47 | +import java.util.stream.IntStream; | ||
41 | 48 | ||
42 | 49 | ||
43 | /** | 50 | /** |
@@ -50,6 +57,9 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -50,6 +57,9 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
50 | protected static final Charset UTF8 = StandardCharsets.UTF_8; | 57 | protected static final Charset UTF8 = StandardCharsets.UTF_8; |
51 | private static final JsonParser parser = new JsonParser(); | 58 | private static final JsonParser parser = new JsonParser(); |
52 | 59 | ||
60 | + private static final String registerCountKey = "registerCountKey"; | ||
61 | + private static final String modelsKey = "modelsKey"; | ||
62 | + | ||
53 | 63 | ||
54 | @Override | 64 | @Override |
55 | public TransportProtos.PostTelemetryMsg convertToPostTelemetry(TcpDeviceWareSessionContext ctx, String payload) throws AdaptorException { | 65 | public TransportProtos.PostTelemetryMsg convertToPostTelemetry(TcpDeviceWareSessionContext ctx, String payload) throws AdaptorException { |
@@ -164,6 +174,352 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -164,6 +174,352 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
164 | return Optional.of(null); | 174 | return Optional.of(null); |
165 | } | 175 | } |
166 | 176 | ||
177 | + @Override | ||
178 | + public Optional<TcpUpEntry> convertModbusHexToPublish(TcpDeviceWareSessionContext ctx, String hexString) { | ||
179 | + List<TkThingsModel> thingsModels = ctx.getDeviceProfile().getProfileData().getThingsModel(); | ||
180 | + TcpUpEntry entry = new TcpUpEntry(UUID.randomUUID()); | ||
181 | + entry.setTelemetry(true); | ||
182 | + ObjectNode convertResult =convertHexToJson(hexString,thingsModels); | ||
183 | + if(null == convertResult){ | ||
184 | + convertResult = JacksonUtil.newObjectNode(); | ||
185 | + } | ||
186 | + //是否包含原始数据标识符 | ||
187 | + if(checkContainsSourceDataIdentify(thingsModels)){ | ||
188 | + convertResult.put(FastIotConstants.ModBusRTU.SOURCE_DATA_IDENTIFY,hexString); | ||
189 | + } | ||
190 | + entry.setDatas(JacksonUtil.convertValue(convertResult,Map.class)); | ||
191 | + return Optional.of(entry); | ||
192 | + } | ||
193 | + | ||
194 | + private ObjectNode convertHexToJson(String hexString, List<TkThingsModel> thingsModels){ | ||
195 | + if(null !=thingsModels && !thingsModels.isEmpty()){ | ||
196 | + //功能码 | ||
197 | + String functionCode = hexString.substring(2, 4); | ||
198 | + //数据长度hex | ||
199 | + String dataLength = hexString.substring(4,6); | ||
200 | + boolean coilOrInputStatus = functionCode.equals("01") || functionCode.equals("02"); | ||
201 | + int byteLength = Integer.parseInt(dataLength,16); | ||
202 | + //返回的数据长度 | ||
203 | + int responseByteLength = coilOrInputStatus?byteLength:byteLength/2; | ||
204 | + //根据功能码,获取匹配的物模型属性,并将连续的寄存器地址物模型分为一个集合,例如 40001、40002 40100、40101、40102 | ||
205 | + Map<String,Map<String,Object>> matchThingsModelsMap = matchFunctionCodeList(functionCode,thingsModels); | ||
206 | + | ||
207 | + for (Map.Entry<String, Map<String, Object>> entry :matchThingsModelsMap.entrySet()){ | ||
208 | + Map<String,Object> matchMap = entry.getValue(); | ||
209 | + //匹配的寄存器数量即物模型属性数量 | ||
210 | + int matchRegisterCount = (int) matchMap.get(registerCountKey); | ||
211 | + if(matchRegisterCount>0){ | ||
212 | + List<TkThingsModel> matchThingsModels = (List<TkThingsModel>) matchMap.get(modelsKey); | ||
213 | + if(coilOrInputStatus){ | ||
214 | + //去掉字节长度后的数据 | ||
215 | + String registerHexData =hexString.substring(6,hexString.length()-4); | ||
216 | + //一个字节8个位,用字节长度*8即可得到对应多少个位的值 | ||
217 | + int bitNumber = responseByteLength * 8; | ||
218 | + if(matchRegisterCount<= bitNumber){ | ||
219 | + //线圈状态和离散量输入的数据读取处理 | ||
220 | + return coilOrInputStatusDataProcess(registerHexData,matchThingsModels); | ||
221 | + } | ||
222 | + }else{ | ||
223 | + //如果连续物模型的长度等于返回的寄存器长度,就用该连续的物模型进行匹配 | ||
224 | + boolean compareResult = matchThingsModelsMap.size()>1?matchRegisterCount == responseByteLength | ||
225 | + :matchRegisterCount <= responseByteLength || responseByteLength <= matchRegisterCount; | ||
226 | + if(compareResult){ | ||
227 | + //保持寄存器和输入寄存器的数据读取处理 | ||
228 | + return holdingOrInputRegisterDataProcess(hexString,matchThingsModels,thingsModels,functionCode); | ||
229 | + } | ||
230 | + } | ||
231 | + } | ||
232 | + } | ||
233 | + } | ||
234 | + return null; | ||
235 | + } | ||
236 | + | ||
237 | + private ObjectNode coilOrInputStatusDataProcess(String registerHexData,List<TkThingsModel> matchThingsModels){ | ||
238 | + if(null != matchThingsModels && !matchThingsModels.isEmpty()){ | ||
239 | + int groupSize = 2; // 设定分组大小 | ||
240 | + //分组后的hex | ||
241 | + List<String> groups = IntStream.range(0, registerHexData.length()) | ||
242 | + .filter(i -> i % groupSize == 0) | ||
243 | + .mapToObj(i -> registerHexData.substring(i, Math.min(registerHexData.length(), i + groupSize))) | ||
244 | + .collect(Collectors.toList()); | ||
245 | + List<Integer> allBits = new ArrayList<>(); | ||
246 | + for (String byteHex : groups){ | ||
247 | + int[] bits = HexConvertUtils.hexToBits(byteHex); | ||
248 | + List<Integer> temp = Arrays.stream(bits).boxed().collect(Collectors.toList()); | ||
249 | + allBits.addAll(temp); | ||
250 | + } | ||
251 | + ObjectNode packageData = JacksonUtil.newObjectNode(); | ||
252 | + matchThingsModels.stream().forEach(model->{ | ||
253 | + //扩展描述信息 | ||
254 | + JsonNode extensionDescInfo = model.getExtensionDesc(); | ||
255 | + //找到物模型的寄存器地址,并转为10进制 | ||
256 | + int registerAddress = Integer.parseInt(String.valueOf(extensionDescInfo.get( | ||
257 | + FastIotConstants.ModBusRTU.REGISTER_ADDRESS).asText()),16); | ||
258 | + packageData.put(model.getIdentifier(), allBits.get(registerAddress)); | ||
259 | + }); | ||
260 | + return packageData; | ||
261 | + } | ||
262 | + return null; | ||
263 | + } | ||
264 | + private boolean checkContainsSourceDataIdentify(List<TkThingsModel> thingsModels){ | ||
265 | + boolean containsSourceDataIdentify = false; | ||
266 | + for (TkThingsModel model : thingsModels){ | ||
267 | + if(model.getIdentifier().equals(FastIotConstants.ModBusRTU.SOURCE_DATA_IDENTIFY)){ | ||
268 | + containsSourceDataIdentify = true; | ||
269 | + break; | ||
270 | + } | ||
271 | + } | ||
272 | + return containsSourceDataIdentify; | ||
273 | + } | ||
274 | + private ObjectNode holdingOrInputRegisterDataProcess(String hexString, List<TkThingsModel> matchThingsModels, | ||
275 | + List<TkThingsModel> sourceThingsModels,String functionCode){ | ||
276 | + Map<String,Object> dataJson = new HashMap<>(); | ||
277 | + //上报hex的寄存器个数 | ||
278 | + int hexRegisterCount = Integer.parseInt(hexString.substring(4,6),16)/2; | ||
279 | + //去掉字节长度后的数据 | ||
280 | + String registerHexData =hexString.substring(6,hexString.length()-4); | ||
281 | + //当前物模型的数据截取起始地址 | ||
282 | + int currentHexStartAddress = 0; | ||
283 | + //处理了的寄存器个数 | ||
284 | + int processCount = 0; | ||
285 | + //开始构造数据结构 | ||
286 | + for (TkThingsModel thingsModel : matchThingsModels){ | ||
287 | + //说明上报的hex数据已经处理完了 | ||
288 | + if(processCount>=hexRegisterCount){ | ||
289 | + break; | ||
290 | + } | ||
291 | + //扩展描述信息 | ||
292 | + JsonNode extensionDescInfo = thingsModel.getExtensionDesc(); | ||
293 | + String identifier = thingsModel.getIdentifier(); | ||
294 | + AttributeSourceDataTypeEnum dataTypeEnum = JacksonUtil.convertValue(extensionDescInfo.get( | ||
295 | + FastIotConstants.ModBusRTU.ORIGINAL_DATA_TYPE).asText(),AttributeSourceDataTypeEnum.class); | ||
296 | + //对数据进行封装 | ||
297 | + //根据物模型的原始数据类型,返回长度 | ||
298 | + int hexLength = ModbusUtils.getDataLengthBySourceDataType(dataTypeEnum); | ||
299 | + | ||
300 | + int bitAddress = 0; | ||
301 | + boolean isBits = dataTypeEnum.equals(AttributeSourceDataTypeEnum.BITS); | ||
302 | + if(isBits){ | ||
303 | + bitAddress = extensionDescInfo.get(FastIotConstants.ModBusRTU.BIT_MASK).asInt(); | ||
304 | + hexLength = 4; | ||
305 | + } | ||
306 | + String hex = registerHexData.substring(currentHexStartAddress,currentHexStartAddress + hexLength); | ||
307 | + currentHexStartAddress += hexLength; | ||
308 | + if(isBits){ | ||
309 | + //位计算,一个寄存器可以表示16个位 | ||
310 | + //找到所有满足的功能码,地址码的物模型 | ||
311 | + int sourceAddress = Integer.parseInt(extensionDescInfo.get(FastIotConstants.ModBusRTU.REGISTER_ADDRESS).asText(),16); | ||
312 | + bitProcess(hex,functionCode,sourceAddress,dataJson,sourceThingsModels); | ||
313 | + }else{ | ||
314 | + float scaleFactor = (1/Float.valueOf(extensionDescInfo.get(FastIotConstants.ModBusRTU.SCALE_FACTOR).asText())); | ||
315 | + Object value = HexConvertUtils.setDeviceTelemetry(hex,dataTypeEnum,bitAddress,scaleFactor); | ||
316 | + if(dataTypeEnum.name().contains("UINT") && Double.valueOf(value.toString())<0){ | ||
317 | + //如果无符号返回的值小于0则不保存 | ||
318 | + continue; | ||
319 | + } | ||
320 | + dataJson.put(identifier,value); | ||
321 | + } | ||
322 | + processCount+=getRegisterCountByDataType(extensionDescInfo | ||
323 | + .get(FastIotConstants.ModBusRTU.ORIGINAL_DATA_TYPE).asText()); | ||
324 | + } | ||
325 | + return JacksonUtil.convertValue(dataJson, ObjectNode.class); | ||
326 | + } | ||
327 | + | ||
328 | + /** | ||
329 | + * 位计算处理,一个寄存器16个位计算 | ||
330 | + * @param hex 16进制数据 | ||
331 | + * @param sourceFunctionCode 功能码 | ||
332 | + * @param sourceAddress 寄存器地址 | ||
333 | + * @param sourceThingsModels 所有的物模型 | ||
334 | + * @return 处理后的json数据格式 | ||
335 | + */ | ||
336 | + private void bitProcess(String hex,String sourceFunctionCode,int sourceAddress,Map<String,Object> dataJson, | ||
337 | + List<TkThingsModel> sourceThingsModels){ | ||
338 | + List<TkThingsModel> matchResult = functionCodeAndAddress(sourceThingsModels,sourceFunctionCode,sourceAddress); | ||
339 | + if(!matchResult.isEmpty()){ | ||
340 | + for (TkThingsModel tkThingsModel : matchResult){ | ||
341 | + int bitAddress = tkThingsModel.getExtensionDesc().get(FastIotConstants.ModBusRTU.BIT_MASK).asInt(); | ||
342 | + Object value = HexConvertUtils.setDeviceTelemetry(hex,AttributeSourceDataTypeEnum.BITS, | ||
343 | + bitAddress,0); | ||
344 | + dataJson.put(tkThingsModel.getIdentifier(),value); | ||
345 | + } | ||
346 | + } | ||
347 | + } | ||
348 | + private List<TkThingsModel> functionCodeAndAddress(List<TkThingsModel> sourceThingsModels, | ||
349 | + String sourceFunctionCode,int sourceAddress){ | ||
350 | + List<TkThingsModel> matchResult = new ArrayList<>(); | ||
351 | + for (TkThingsModel thingsModel : sourceThingsModels){ | ||
352 | + TextNode operationType = (TextNode) thingsModel.getExtensionDesc().get(FastIotConstants.ModBusRTU.OPERATION_TYPE); | ||
353 | + if(null != operationType && !StringUtils.isEmpty(operationType.asText())){ | ||
354 | + //功能码,根据不同情况进行截取 | ||
355 | + String[] codes = operationType.asText().split("_"); | ||
356 | + //rw r w 这三种情况 | ||
357 | + String readWrite = codes[1]; | ||
358 | + //是否包含r操作 | ||
359 | + if(readWrite.contains("r")){ | ||
360 | + String checkFunctionCode = codes[2]; | ||
361 | + if(sourceFunctionCode.equals(checkFunctionCode)){ | ||
362 | + //获取寄存器地址,将16进制hex转为10进制 | ||
363 | + TextNode registerAddress = (TextNode) thingsModel.getExtensionDesc().get(FastIotConstants.ModBusRTU.REGISTER_ADDRESS); | ||
364 | + if(null != registerAddress && !StringUtils.isEmpty(registerAddress.asText())){ | ||
365 | + int address = Integer.parseInt(registerAddress.asText(),16); | ||
366 | + if(address == sourceAddress){ | ||
367 | + matchResult.add(thingsModel); | ||
368 | + } | ||
369 | + } | ||
370 | + } | ||
371 | + } | ||
372 | + } | ||
373 | + } | ||
374 | + return matchResult; | ||
375 | + } | ||
376 | + /** | ||
377 | + * 根据上报的功能码与物模型里面的功能码进行匹配,并将连续的寄存器地址进行合并 | ||
378 | + * 存在以下这种情况: | ||
379 | + * 40001、40002 40100、40101、40102 | ||
380 | + * @param functionCode 上报的功能码 | ||
381 | + * @param thingsModels 物模型 | ||
382 | + * @return 返回的数据结构 | ||
383 | + * [{ | ||
384 | + * "1group": { | ||
385 | + * "registerCountKey": 2,//寄存器数量 | ||
386 | + * "modelsKey": [TKThingsModel,TKThingsModel] | ||
387 | + * } | ||
388 | + * }, | ||
389 | + * { | ||
390 | + * "2group": { | ||
391 | + * "registerCountKey": 2,//寄存器数量 | ||
392 | + * "modelsKey": [TKThingsModel,TKThingsModel] | ||
393 | + * } | ||
394 | + * } | ||
395 | + * ] | ||
396 | + */ | ||
397 | + private Map<String,Map<String,Object>> matchFunctionCodeList( | ||
398 | + String functionCode, List<TkThingsModel> thingsModels) { | ||
399 | + //按连续寄存器地址进行分组的map | ||
400 | + Map<String,Map<String,Object>> groupMap = new HashMap<>(); | ||
401 | + List<Map.Entry<Integer, TkThingsModel>> matchFunctionModels = matchFunctionCode(thingsModels,functionCode); | ||
402 | + //按连续寄存器地址进行区分 | ||
403 | + if(!matchFunctionModels.isEmpty()){ | ||
404 | + int flag = -1; | ||
405 | + String prevDataType = null; | ||
406 | + List<TkThingsModel> modelsList = null; | ||
407 | + int registerCount = 0; | ||
408 | + Map<String,Object> tempMap = new HashMap<>(); | ||
409 | + String groupKey = "group"; | ||
410 | + for(Map.Entry<Integer, TkThingsModel> orderMap :matchFunctionModels){ | ||
411 | + TkThingsModel thingsModel = orderMap.getValue(); | ||
412 | + int key = orderMap.getKey(); | ||
413 | + if(null==prevDataType){ | ||
414 | + prevDataType = thingsModel.getExtensionDesc() | ||
415 | + .get(FastIotConstants.ModBusRTU.ORIGINAL_DATA_TYPE).asText(); | ||
416 | + } | ||
417 | + if(flag!=-1 && null != modelsList){ | ||
418 | + //默认寄存器地址+1 | ||
419 | + int add = getRegisterCountByDataType(prevDataType); | ||
420 | + //如果当前key是连续寄存器地址,择直接存入temp里面 | ||
421 | + if(key == flag + add){ | ||
422 | + Map<String,Object> saveMap = groupMap.get(groupKey); | ||
423 | + modelsList = (List<TkThingsModel>) saveMap.get(modelsKey); | ||
424 | + registerCount = (int) saveMap.get(registerCountKey); | ||
425 | + modelsList.add(thingsModel); | ||
426 | + //寄存器数量,直接加上当前 | ||
427 | + registerCount +=getRegisterCountByDataType(thingsModel.getExtensionDesc() | ||
428 | + .get(FastIotConstants.ModBusRTU.ORIGINAL_DATA_TYPE).asText()); | ||
429 | + flag = key; | ||
430 | + }else{ | ||
431 | + //重置临时map为新的对象 | ||
432 | + tempMap = new HashMap<>(); | ||
433 | + modelsList = new ArrayList<>(); | ||
434 | + registerCount = 0; | ||
435 | + modelsList.add(thingsModel); | ||
436 | + prevDataType = thingsModel.getExtensionDesc() | ||
437 | + .get(FastIotConstants.ModBusRTU.ORIGINAL_DATA_TYPE).asText(); | ||
438 | + registerCount +=getRegisterCountByDataType(prevDataType); | ||
439 | + flag = key; | ||
440 | + groupKey+=groupMap.size(); | ||
441 | + } | ||
442 | + }else{ | ||
443 | + //初始化每个分组的信息 | ||
444 | + modelsList = new ArrayList<>(); | ||
445 | + modelsList.add(thingsModel); | ||
446 | + registerCount +=getRegisterCountByDataType(prevDataType); | ||
447 | + flag = key; | ||
448 | + groupKey+=groupMap.size(); | ||
449 | + } | ||
450 | + prevDataType = thingsModel.getExtensionDesc().get(FastIotConstants.ModBusRTU.ORIGINAL_DATA_TYPE).asText(); | ||
451 | + tempMap.put(registerCountKey,registerCount); | ||
452 | + tempMap.put(modelsKey,modelsList); | ||
453 | + groupMap.put(groupKey,tempMap); | ||
454 | + } | ||
455 | + } | ||
456 | + return groupMap.size()==1?groupMap:orderByMatchMap(groupMap); | ||
457 | + } | ||
458 | + private Map<String,Map<String,Object>> orderByMatchMap(Map<String,Map<String,Object>> groupMap){ | ||
459 | + // 使用自定义的Comparator按count排序 | ||
460 | + return groupMap.entrySet().stream() | ||
461 | + .sorted(Map.Entry.comparingByValue((v1, v2) -> | ||
462 | + v1.get(registerCountKey).equals(v2.get(registerCountKey)) ? 0 : | ||
463 | + Integer.compare(Integer.valueOf(v2.get(registerCountKey).toString()), | ||
464 | + Integer.valueOf(v1.get(registerCountKey).toString())))) | ||
465 | + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, | ||
466 | + (oldValue, newValue) -> oldValue, LinkedHashMap::new)); | ||
467 | + } | ||
468 | + private int getRegisterCountByDataType(String dataType){ | ||
469 | + int registerCount = 1; | ||
470 | + if(dataType.contains("INT32") || dataType.contains("FLOAT")){ | ||
471 | + registerCount = 2; | ||
472 | + } | ||
473 | + if(dataType.contains("INT16")||dataType.equals(AttributeSourceDataTypeEnum.BOOLEAN.name()) | ||
474 | + ||dataType.equals(AttributeSourceDataTypeEnum.BITS.name())){ | ||
475 | + registerCount = 1; | ||
476 | + } | ||
477 | + if(dataType.equals(AttributeSourceDataTypeEnum.DOUBLE.name())){ | ||
478 | + registerCount =4; | ||
479 | + } | ||
480 | + return registerCount; | ||
481 | + } | ||
482 | + | ||
483 | + /** | ||
484 | + * 根据上报的功能码与物模型里面的功能码进行匹配,并按寄存器进行排序 | ||
485 | + * 存在以下几种情况: | ||
486 | + * holdingRegister_w_06 | ||
487 | + * holdingRegister_r_03 | ||
488 | + * holdingRegister_rw_03_06 | ||
489 | + * @param functionCode 上报的功能码 | ||
490 | + * @param thingsModels 物模型 | ||
491 | + * @return 匹配的物模型 | ||
492 | + */ | ||
493 | + private List<Map.Entry<Integer, TkThingsModel>> matchFunctionCode(List<TkThingsModel> thingsModels,String functionCode){ | ||
494 | + Map<Integer, TkThingsModel> matchThingsModel = new HashMap<>(); | ||
495 | + for (TkThingsModel thingsModel : thingsModels){ | ||
496 | + TextNode operationType = (TextNode) thingsModel.getExtensionDesc().get(FastIotConstants.ModBusRTU.OPERATION_TYPE); | ||
497 | + if(null != operationType && !StringUtils.isEmpty(operationType.asText())){ | ||
498 | + //功能码,根据不同情况进行截取 | ||
499 | + String[] codes = operationType.asText().split("_"); | ||
500 | + //rw r w 这三种情况 | ||
501 | + String readWrite = codes[1]; | ||
502 | + //是否包含r操作 | ||
503 | + if(readWrite.contains("r")){ | ||
504 | + String checkFunctionCode = codes[2]; | ||
505 | + if(functionCode.equals(checkFunctionCode)){ | ||
506 | + //获取寄存器地址,将16进制hex转为10进制 | ||
507 | + TextNode registerAddress = (TextNode) thingsModel.getExtensionDesc().get(FastIotConstants.ModBusRTU.REGISTER_ADDRESS); | ||
508 | + if(null != registerAddress && !StringUtils.isEmpty(registerAddress.asText())){ | ||
509 | + int address = Integer.parseInt(registerAddress.asText(),16); | ||
510 | + matchThingsModel.put(address,thingsModel); | ||
511 | + } | ||
512 | + } | ||
513 | + } | ||
514 | + } | ||
515 | + } | ||
516 | + // 将Map转换为List | ||
517 | + List<Map.Entry<Integer, TkThingsModel>> list = new ArrayList<>(matchThingsModel.entrySet()); | ||
518 | + // 对List进行排序 | ||
519 | + Collections.sort(list, Map.Entry.comparingByKey()); | ||
520 | + return list; | ||
521 | + } | ||
522 | + | ||
167 | public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { | 523 | public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { |
168 | String payload = validatePayload(sessionId, payloadData, false); | 524 | String payload = validatePayload(sessionId, payloadData, false); |
169 | try { | 525 | try { |
@@ -267,6 +623,4 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -267,6 +623,4 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
267 | // return new MqttPublishMessage(mqttFixedHeader, header, payload); | 623 | // return new MqttPublishMessage(mqttFixedHeader, header, payload); |
268 | return null; | 624 | return null; |
269 | } | 625 | } |
270 | - | ||
271 | - | ||
272 | } | 626 | } |
@@ -78,6 +78,8 @@ public interface TcpTransportAdaptor { | @@ -78,6 +78,8 @@ public interface TcpTransportAdaptor { | ||
78 | 78 | ||
79 | Optional<TcpUpEntry> convertToPublish(TcpDeviceWareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException; | 79 | Optional<TcpUpEntry> convertToPublish(TcpDeviceWareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException; |
80 | 80 | ||
81 | + Optional<TcpUpEntry> convertModbusHexToPublish(TcpDeviceWareSessionContext ctx,String hexString); | ||
82 | + | ||
81 | public static byte[] toBytes(ByteBuf inbound) { | 83 | public static byte[] toBytes(ByteBuf inbound) { |
82 | byte[] bytes = new byte[inbound.readableBytes()]; | 84 | byte[] bytes = new byte[inbound.readableBytes()]; |
83 | int readerIndex = inbound.readerIndex(); | 85 | int readerIndex = inbound.readerIndex(); |
1 | package org.thingsboard.server.transport.tcp.session; | 1 | package org.thingsboard.server.transport.tcp.session; |
2 | 2 | ||
3 | +import com.fasterxml.jackson.databind.JsonNode; | ||
4 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
5 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
3 | import com.google.common.util.concurrent.FutureCallback; | 6 | import com.google.common.util.concurrent.FutureCallback; |
4 | import com.google.common.util.concurrent.Futures; | 7 | import com.google.common.util.concurrent.Futures; |
5 | import com.google.common.util.concurrent.ListenableFuture; | 8 | import com.google.common.util.concurrent.ListenableFuture; |
6 | import com.google.common.util.concurrent.MoreExecutors; | 9 | import com.google.common.util.concurrent.MoreExecutors; |
7 | import lombok.Getter; | 10 | import lombok.Getter; |
11 | +import lombok.Setter; | ||
8 | import lombok.extern.slf4j.Slf4j; | 12 | import lombok.extern.slf4j.Slf4j; |
9 | import org.apache.commons.lang3.StringUtils; | 13 | import org.apache.commons.lang3.StringUtils; |
10 | import org.jetbrains.annotations.Nullable; | 14 | import org.jetbrains.annotations.Nullable; |
@@ -12,6 +16,8 @@ import org.thingsboard.common.util.JacksonUtil; | @@ -12,6 +16,8 @@ import org.thingsboard.common.util.JacksonUtil; | ||
12 | import org.thingsboard.server.common.data.DeviceProfile; | 16 | import org.thingsboard.server.common.data.DeviceProfile; |
13 | import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | 17 | import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; |
14 | import org.thingsboard.server.common.data.device.profile.TkTcpDeviceProfileTransportConfiguration; | 18 | import org.thingsboard.server.common.data.device.profile.TkTcpDeviceProfileTransportConfiguration; |
19 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
20 | +import org.thingsboard.server.common.data.yunteng.enums.ProtocolAnalysisEnum; | ||
15 | import org.thingsboard.server.common.data.yunteng.enums.TkScriptFunctionType; | 21 | import org.thingsboard.server.common.data.yunteng.enums.TkScriptFunctionType; |
16 | import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | 22 | import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; |
17 | import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; | 23 | import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; |
@@ -40,7 +46,14 @@ public abstract class TcpDeviceWareSessionContext extends DeviceAwareSessionCont | @@ -40,7 +46,14 @@ public abstract class TcpDeviceWareSessionContext extends DeviceAwareSessionCont | ||
40 | private volatile UUID telemetryScriptId; | 46 | private volatile UUID telemetryScriptId; |
41 | @Getter | 47 | @Getter |
42 | private volatile UUID rpcScriptId; | 48 | private volatile UUID rpcScriptId; |
43 | - private final ConcurrentMap<String, Integer> rpcAwaitingAck; | 49 | + |
50 | + /** | ||
51 | + * 设备地址码或设备标识符 | ||
52 | + */ | ||
53 | + @Getter | ||
54 | + private volatile String deviceCode; | ||
55 | + private final ConcurrentMap<String | ||
56 | + , Integer> rpcAwaitingAck; | ||
44 | /** | 57 | /** |
45 | * 设备唯一标识符,例如:设备SN、设备地址码等。数据内携带标识符 | 58 | * 设备唯一标识符,例如:设备SN、设备地址码等。数据内携带标识符 |
46 | */ | 59 | */ |
@@ -65,6 +78,7 @@ public abstract class TcpDeviceWareSessionContext extends DeviceAwareSessionCont | @@ -65,6 +78,7 @@ public abstract class TcpDeviceWareSessionContext extends DeviceAwareSessionCont | ||
65 | public void setDeviceInfo(TransportDeviceInfo deviceInfo) { | 78 | public void setDeviceInfo(TransportDeviceInfo deviceInfo) { |
66 | super.setDeviceInfo(deviceInfo); | 79 | super.setDeviceInfo(deviceInfo); |
67 | deviceName = deviceInfo.getDeviceName(); | 80 | deviceName = deviceInfo.getDeviceName(); |
81 | + setDeviceCode(deviceInfo.getAdditionalInfo()); | ||
68 | } | 82 | } |
69 | 83 | ||
70 | @Override | 84 | @Override |
@@ -86,10 +100,25 @@ public abstract class TcpDeviceWareSessionContext extends DeviceAwareSessionCont | @@ -86,10 +100,25 @@ public abstract class TcpDeviceWareSessionContext extends DeviceAwareSessionCont | ||
86 | if(null != tcpConfiguration && StringUtils.isNotEmpty(tcpConfiguration.getAuthScriptId())){ | 100 | if(null != tcpConfiguration && StringUtils.isNotEmpty(tcpConfiguration.getAuthScriptId())){ |
87 | this.authScriptId = UUID.fromString(tcpConfiguration.getAuthScriptId()); | 101 | this.authScriptId = UUID.fromString(tcpConfiguration.getAuthScriptId()); |
88 | } | 102 | } |
89 | - this.telemetryScriptId = UUID.fromString(tcpConfiguration.getUpScriptId()); | 103 | + if( tcpConfiguration.getProtocol().equals(ProtocolAnalysisEnum.CUSTOM)){ |
104 | + this.telemetryScriptId = UUID.fromString(tcpConfiguration.getUpScriptId()); | ||
105 | + } | ||
90 | 106 | ||
91 | } | 107 | } |
92 | 108 | ||
109 | + public void setDeviceCode(String additionalInfo){ | ||
110 | + try{ | ||
111 | + ObjectMapper objectMapper = new ObjectMapper(); | ||
112 | + JsonNode additionalInfoJson = objectMapper.readTree(additionalInfo); | ||
113 | + if(null != additionalInfoJson && !additionalInfoJson.isEmpty()){ | ||
114 | + deviceCode = String.valueOf(additionalInfoJson.get(FastIotConstants.TCP_DEVICE_IDENTIFY_FILED)==null? | ||
115 | + null:additionalInfoJson.get(FastIotConstants.TCP_DEVICE_IDENTIFY_FILED).asText()); | ||
116 | + } | ||
117 | + }catch (Exception e){ | ||
118 | + log.error("device [{}] convert additionalInfo error",deviceName); | ||
119 | + } | ||
120 | + } | ||
121 | + | ||
93 | 122 | ||
94 | 123 | ||
95 | /** | 124 | /** |
@@ -33,6 +33,8 @@ import org.springframework.util.ConcurrentReferenceHashMap; | @@ -33,6 +33,8 @@ import org.springframework.util.ConcurrentReferenceHashMap; | ||
33 | import org.springframework.util.StringUtils; | 33 | import org.springframework.util.StringUtils; |
34 | import org.thingsboard.common.util.JacksonUtil; | 34 | import org.thingsboard.common.util.JacksonUtil; |
35 | import org.thingsboard.server.common.data.DeviceProfile; | 35 | import org.thingsboard.server.common.data.DeviceProfile; |
36 | +import org.thingsboard.server.common.data.device.profile.TkTcpDeviceProfileTransportConfiguration; | ||
37 | +import org.thingsboard.server.common.data.yunteng.enums.ProtocolAnalysisEnum; | ||
36 | import org.thingsboard.server.common.data.yunteng.utils.ByteUtils; | 38 | import org.thingsboard.server.common.data.yunteng.utils.ByteUtils; |
37 | import org.thingsboard.server.common.transport.TransportService; | 39 | import org.thingsboard.server.common.transport.TransportService; |
38 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 40 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
@@ -44,6 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFro | @@ -44,6 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFro | ||
44 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | 46 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
45 | import org.thingsboard.server.transport.tcp.TcpTransportContext; | 47 | import org.thingsboard.server.transport.tcp.TcpTransportContext; |
46 | import org.thingsboard.server.transport.tcp.adaptors.TcpTransportAdaptor; | 48 | import org.thingsboard.server.transport.tcp.adaptors.TcpTransportAdaptor; |
49 | +import org.thingsboard.server.transport.tcp.adaptors.TcpUpEntry; | ||
47 | import org.thingsboard.server.transport.tcp.util.ByteBufUtils; | 50 | import org.thingsboard.server.transport.tcp.util.ByteBufUtils; |
48 | 51 | ||
49 | import javax.annotation.Nullable; | 52 | import javax.annotation.Nullable; |
@@ -94,19 +97,24 @@ public class TcpGatewaySessionHandler { | @@ -94,19 +97,24 @@ public class TcpGatewaySessionHandler { | ||
94 | } | 97 | } |
95 | 98 | ||
96 | 99 | ||
97 | - public void onDeviceTelemetry(String deviceName, UUID requestId, String deviceDataStr) { | 100 | + public void onDeviceTelemetry(String deviceName, UUID requestId, String sourceHexData, ProtocolAnalysisEnum analysisEnum) { |
98 | Futures.addCallback(checkDeviceConnected(deviceName), | 101 | Futures.addCallback(checkDeviceConnected(deviceName), |
99 | new FutureCallback<>() { | 102 | new FutureCallback<>() { |
100 | @Override | 103 | @Override |
101 | public void onSuccess(@Nullable TcpGatewayDeviceSessionCtx deviceCtx) { | 104 | public void onSuccess(@Nullable TcpGatewayDeviceSessionCtx deviceCtx) { |
102 | - deviceCtx.doUpScript(deviceDataStr, r -> { | ||
103 | - try { | ||
104 | - TransportProtos.PostTelemetryMsg postTelemetryMsg = deviceCtx.getPayloadAdaptor().convertToPostTelemetry(deviceCtx, JacksonUtil.toString(r.getDatas())); | ||
105 | - processPostTelemetryMsg(deviceCtx, postTelemetryMsg, deviceName, requestId); | ||
106 | - } catch (AdaptorException e) { | ||
107 | - log.warn("[{}][{}] Failed to convert telemetry: {}", gateway.getDeviceId(), deviceName, deviceDataStr, e); | ||
108 | - } | ||
109 | - }); | 105 | + switch (analysisEnum){ |
106 | + case CUSTOM: | ||
107 | + deviceCtx.doUpScript(sourceHexData, r -> { | ||
108 | + processDeviceTelemetry(deviceCtx,deviceName,requestId,sourceHexData,JacksonUtil.toString(r.getDatas())); | ||
109 | + }); | ||
110 | + break; | ||
111 | + case MODBUS_RTU: | ||
112 | + TcpUpEntry entry = deviceCtx.getPayloadAdaptor().convertModbusHexToPublish(deviceCtx,sourceHexData).get(); | ||
113 | + if(null != entry && !entry.getDatas().isEmpty()){ | ||
114 | + processDeviceTelemetry(deviceCtx,deviceName,requestId,sourceHexData,JacksonUtil.toString(entry.getDatas())); | ||
115 | + } | ||
116 | + break; | ||
117 | + } | ||
110 | } | 118 | } |
111 | 119 | ||
112 | @Override | 120 | @Override |
@@ -116,6 +124,16 @@ public class TcpGatewaySessionHandler { | @@ -116,6 +124,16 @@ public class TcpGatewaySessionHandler { | ||
116 | }, context.getExecutor()); | 124 | }, context.getExecutor()); |
117 | } | 125 | } |
118 | 126 | ||
127 | + private void processDeviceTelemetry (TcpGatewayDeviceSessionCtx deviceCtx,String deviceName, UUID requestId, String sourceHexData, | ||
128 | + String telemetryData){ | ||
129 | + try { | ||
130 | + TransportProtos.PostTelemetryMsg postTelemetryMsg = deviceCtx.getPayloadAdaptor().convertToPostTelemetry(deviceCtx, telemetryData); | ||
131 | + processPostTelemetryMsg(deviceCtx, postTelemetryMsg, deviceName, requestId); | ||
132 | + } catch (AdaptorException e) { | ||
133 | + log.warn("[{}][{}] Failed to convert telemetry: {}", gateway.getDeviceId(), deviceName, sourceHexData, e); | ||
134 | + } | ||
135 | + } | ||
136 | + | ||
119 | 137 | ||
120 | public void onGatewayDisconnect() { | 138 | public void onGatewayDisconnect() { |
121 | devices.forEach(this::deregisterSession); | 139 | devices.forEach(this::deregisterSession); |
@@ -332,6 +350,7 @@ public class TcpGatewaySessionHandler { | @@ -332,6 +350,7 @@ public class TcpGatewaySessionHandler { | ||
332 | 350 | ||
333 | 351 | ||
334 | private void deregisterSession(String deviceName, TcpGatewayDeviceSessionCtx deviceSessionCtx) { | 352 | private void deregisterSession(String deviceName, TcpGatewayDeviceSessionCtx deviceSessionCtx) { |
353 | + devices.remove(deviceName); | ||
335 | transportService.deregisterSession(deviceSessionCtx.getSessionInfo()); | 354 | transportService.deregisterSession(deviceSessionCtx.getSessionInfo()); |
336 | transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_CLOSED, null); | 355 | transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_CLOSED, null); |
337 | log.debug("[{}] Removed device [{}] from the gateway session", sessionId, deviceName); | 356 | log.debug("[{}] Removed device [{}] from the gateway session", sessionId, deviceName); |
@@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; | @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; | ||
47 | import org.thingsboard.server.common.data.device.profile.TkTcpDeviceProfileTransportConfiguration; | 47 | import org.thingsboard.server.common.data.device.profile.TkTcpDeviceProfileTransportConfiguration; |
48 | import org.thingsboard.server.common.data.id.DeviceId; | 48 | import org.thingsboard.server.common.data.id.DeviceId; |
49 | import org.thingsboard.server.common.data.rpc.RpcStatus; | 49 | import org.thingsboard.server.common.data.rpc.RpcStatus; |
50 | +import org.thingsboard.server.common.data.yunteng.enums.ProtocolAnalysisEnum; | ||
50 | import org.thingsboard.server.common.data.yunteng.utils.ByteUtils; | 51 | import org.thingsboard.server.common.data.yunteng.utils.ByteUtils; |
51 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; | 52 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; |
52 | import org.thingsboard.server.common.transport.SessionMsgListener; | 53 | import org.thingsboard.server.common.transport.SessionMsgListener; |
@@ -175,7 +176,7 @@ public class UdpDatagramDataHandler | @@ -175,7 +176,7 @@ public class UdpDatagramDataHandler | ||
175 | } | 176 | } |
176 | if (tcpMessage.getTelemetry()) { | 177 | if (tcpMessage.getTelemetry()) { |
177 | gatewaySessionHandler.onDeviceTelemetry( | 178 | gatewaySessionHandler.onDeviceTelemetry( |
178 | - devName, tcpMessage.getRequestId(), param.toString()); | 179 | + devName, tcpMessage.getRequestId(), param.toString(), ProtocolAnalysisEnum.CUSTOM); |
179 | } else { | 180 | } else { |
180 | // gatewaySessionHandler.onDeviceRpcResponse(devName, | 181 | // gatewaySessionHandler.onDeviceRpcResponse(devName, |
181 | // tcpMessage.getRequestId(), param.toString()); | 182 | // tcpMessage.getRequestId(), param.toString()); |
@@ -16,15 +16,11 @@ import org.thingsboard.server.common.data.yunteng.constant.QueryConstant; | @@ -16,15 +16,11 @@ import org.thingsboard.server.common.data.yunteng.constant.QueryConstant; | ||
16 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | 16 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; |
17 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | 17 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; |
18 | import org.thingsboard.server.common.data.yunteng.dto.*; | 18 | import org.thingsboard.server.common.data.yunteng.dto.*; |
19 | -import org.thingsboard.server.common.data.yunteng.enums.StatusEnum; | ||
20 | -import org.thingsboard.server.common.data.yunteng.enums.TkModBusFunctionCode; | ||
21 | -import org.thingsboard.server.common.data.yunteng.enums.TkScriptFunctionType; | ||
22 | -import org.thingsboard.server.common.data.yunteng.enums.TransportTypeEnum; | 19 | +import org.thingsboard.server.common.data.yunteng.enums.*; |
23 | import org.thingsboard.server.common.data.yunteng.utils.ByteUtils; | 20 | import org.thingsboard.server.common.data.yunteng.utils.ByteUtils; |
24 | import org.thingsboard.server.common.data.yunteng.utils.CrcUtils; | 21 | import org.thingsboard.server.common.data.yunteng.utils.CrcUtils; |
25 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | 22 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
26 | import org.thingsboard.server.dao.yunteng.entities.TkDeviceScriptEntity; | 23 | import org.thingsboard.server.dao.yunteng.entities.TkDeviceScriptEntity; |
27 | -import org.thingsboard.server.dao.yunteng.mapper.TkDeviceProfileMapper; | ||
28 | import org.thingsboard.server.dao.yunteng.mapper.TkDeviceScriptMapper; | 24 | import org.thingsboard.server.dao.yunteng.mapper.TkDeviceScriptMapper; |
29 | import org.thingsboard.server.dao.yunteng.service.*; | 25 | import org.thingsboard.server.dao.yunteng.service.*; |
30 | 26 | ||
@@ -39,7 +35,6 @@ public class TkDeviceScriptServiceImpl | @@ -39,7 +35,6 @@ public class TkDeviceScriptServiceImpl | ||
39 | extends AbstractBaseService<TkDeviceScriptMapper, TkDeviceScriptEntity> | 35 | extends AbstractBaseService<TkDeviceScriptMapper, TkDeviceScriptEntity> |
40 | implements TkDeviceScriptService { | 36 | implements TkDeviceScriptService { |
41 | 37 | ||
42 | - private final TkDeviceProfileMapper profileMapper; | ||
43 | private final TkCustomerDevice tkCustomerDevice; | 38 | private final TkCustomerDevice tkCustomerDevice; |
44 | private final TkDeviceProfileService tkDeviceProfileService; | 39 | private final TkDeviceProfileService tkDeviceProfileService; |
45 | private final TkDeviceService tkDeviceService; | 40 | private final TkDeviceService tkDeviceService; |
@@ -322,8 +317,6 @@ public class TkDeviceScriptServiceImpl | @@ -322,8 +317,6 @@ public class TkDeviceScriptServiceImpl | ||
322 | 317 | ||
323 | return result.toString(); | 318 | return result.toString(); |
324 | } | 319 | } |
325 | - | ||
326 | - | ||
327 | private String modelHex(Integer max){ | 320 | private String modelHex(Integer max){ |
328 | String addrStr = ByteUtils.integerToHex(max); | 321 | String addrStr = ByteUtils.integerToHex(max); |
329 | return addrStr.substring(addrStr.length()-4); | 322 | return addrStr.substring(addrStr.length()-4); |
@@ -48,6 +48,7 @@ | @@ -48,6 +48,7 @@ | ||
48 | javaType="org.thingsboard.server.common.data.yunteng.dto.DeviceProfileDTO"> | 48 | javaType="org.thingsboard.server.common.data.yunteng.dto.DeviceProfileDTO"> |
49 | <result property="name" column="profile_name"/> | 49 | <result property="name" column="profile_name"/> |
50 | <result property="transportType" column="transport_type"/> | 50 | <result property="transportType" column="transport_type"/> |
51 | + <result property="profileData" column="profile_data" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/> | ||
51 | </association> | 52 | </association> |
52 | <association property="organizationDTO" | 53 | <association property="organizationDTO" |
53 | javaType="org.thingsboard.server.common.data.yunteng.dto.OrganizationDTO"> | 54 | javaType="org.thingsboard.server.common.data.yunteng.dto.OrganizationDTO"> |
@@ -83,7 +84,7 @@ | @@ -83,7 +84,7 @@ | ||
83 | </sql> | 84 | </sql> |
84 | <sql id="detailColumns"> | 85 | <sql id="detailColumns"> |
85 | <include refid="pageColumns"/> | 86 | <include refid="pageColumns"/> |
86 | - ,idg.name gateway_name,idg.alias gateway_alias | 87 | + ,idg.name gateway_name,idg.alias gateway_alias,ifdp.profile_data |
87 | </sql> | 88 | </sql> |
88 | <sql id="pageColumns"> | 89 | <sql id="pageColumns"> |
89 | <include refid="basicColumns"/> | 90 | <include refid="basicColumns"/> |