diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java index 31f02ef4..0b4e3def 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java @@ -2,6 +2,7 @@ package com.fuyuanshen.app.controller; import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo; import com.fuyuanshen.app.domain.dto.APPReNameDTO; +import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto; import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo; import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo; import com.fuyuanshen.app.service.AppDeviceBizService; @@ -18,6 +19,7 @@ import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -105,4 +107,19 @@ public class AppDeviceController extends BaseController { public R sendMessage(@RequestBody AppDeviceSendMsgBo bo) { return toAjax(appDeviceService.sendMessage(bo)); } + + /** + * 上传设备logo图片 + */ + @PostMapping("/uploadLogo") + public R upload(@Validated @ModelAttribute AppDeviceLogoUploadDto bo) { + + MultipartFile file = bo.getFile(); + if(file.getSize()>1024*1024*2){ + return R.warn("图片不能大于2M"); + } + appDeviceService.uploadDeviceLogo(bo); + + return R.ok(); + } } diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppDeviceLogoUploadDto.java b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppDeviceLogoUploadDto.java new file mode 100644 index 00000000..76dc5e2c --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppDeviceLogoUploadDto.java @@ -0,0 +1,16 @@ +package com.fuyuanshen.app.domain.dto; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +@Data +public class AppDeviceLogoUploadDto { + + private Long deviceId; + + /** + * 文件 + */ + private MultipartFile file; + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceBizService.java b/fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceBizService.java index 0f1a83d2..66b93eed 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceBizService.java +++ b/fys-admin/src/main/java/com/fuyuanshen/app/service/AppDeviceBizService.java @@ -4,19 +4,26 @@ import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.app.domain.AppDeviceBindRecord; import com.fuyuanshen.app.domain.AppPersonnelInfo; import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo; import com.fuyuanshen.app.domain.dto.APPReNameDTO; +import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto; import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo; +import com.fuyuanshen.app.domain.vo.AppDeviceBindRecordVo; import com.fuyuanshen.app.domain.vo.AppDeviceDetailVo; import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo; +import com.fuyuanshen.app.mapper.AppDeviceBindRecordMapper; import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper; import com.fuyuanshen.app.mapper.equipment.APPDeviceMapper; +import com.fuyuanshen.common.core.domain.R; import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.ImageToCArrayConverter; import com.fuyuanshen.common.core.utils.MapstructUtils; import com.fuyuanshen.common.core.utils.ObjectUtils; import com.fuyuanshen.common.mybatis.core.page.PageQuery; import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.redis.utils.RedisUtils; import com.fuyuanshen.common.satoken.utils.AppLoginHelper; import com.fuyuanshen.equipment.domain.Device; import com.fuyuanshen.equipment.domain.DeviceType; @@ -28,17 +35,20 @@ import com.fuyuanshen.equipment.enums.BindingStatusEnum; import com.fuyuanshen.equipment.enums.CommunicationModeEnum; import com.fuyuanshen.equipment.mapper.DeviceMapper; import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; + +import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.*; +import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal; + import com.fuyuanshen.equipment.utils.c.ReliableTextToBitmap; import com.fuyuanshen.system.mqtt.config.MqttGateway; import com.fuyuanshen.system.mqtt.constants.MqttConstants; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.time.Duration; +import java.util.*; @Slf4j @@ -51,6 +61,7 @@ public class AppDeviceBizService { private final AppPersonnelInfoMapper appPersonnelInfoMapper; private final DeviceTypeMapper deviceTypeMapper; private final MqttGateway mqttGateway; + private final AppDeviceBindRecordMapper appDeviceBindRecordMapper; public List getTypeList() { @@ -79,11 +90,13 @@ public class AppDeviceBizService { } byte[] msg = ReliableTextToBitmap.textToBitmapBytes(bo.getSendMsg()); - Map linkHashMap = new HashMap<>(); - linkHashMap.put("message",msg); - String sendMsg = JSON.toJSONString(linkHashMap); - mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+deviceObj.getDeviceImei(), 1 ,sendMsg); - log.info("发送设备消息:{}", bo.getSendMsg()); + ArrayList intData = new ArrayList<>(); + intData.add(2); + buildArr(convertHexToDecimal(msg),intData); + Map map = new HashMap<>(); + map.put("instruct", intData); + mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+deviceObj.getDeviceImei(), 1 ,JSON.toJSONString(map)); + log.info("发送设备消息:topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+deviceObj.getDeviceImei(),bo.getSendMsg()); UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", deviceId) @@ -120,13 +133,32 @@ public class AppDeviceBizService { if (device.getBindingStatus() != null && device.getBindingStatus() == BindingStatusEnum.BOUND.getCode()) { throw new RuntimeException("设备已绑定"); } + + QueryWrapper bindRecordQueryWrapper = new QueryWrapper<>(); + bindRecordQueryWrapper.eq("device_id", device.getId()); + AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper); + if (appDeviceBindRecord != null) { + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("device_id", device.getId()) + .set("binding_status", BindingStatusEnum.BOUND.getCode()) + .set("binding_user_id", userId) + .set("update_time", new Date()) + .set("binding_time", new Date()); + return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper); + }else{ + AppDeviceBindRecord bindRecord = new AppDeviceBindRecord(); + bindRecord.setDeviceId(device.getId()); + bindRecord.setBindingUserId(userId); + bindRecord.setBindingTime(new Date()); + bindRecord.setCreateBy(userId); + appDeviceBindRecordMapper.insert(bindRecord); + } + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); deviceUpdateWrapper.eq("id", device.getId()) .set("binding_status", BindingStatusEnum.BOUND.getCode()) .set("binding_user_id", userId) .set("binding_time", new Date()); - - return deviceMapper.update(null, deviceUpdateWrapper); } else if (mode == CommunicationModeEnum.BLUETOOTH.getValue()) { String deviceMac = bo.getDeviceMac(); @@ -137,9 +169,27 @@ public class AppDeviceBizService { throw new RuntimeException("请先将设备入库!!!"); } Device device = devices.get(0); - if (device.getBindingStatus() != null && device.getBindingStatus() == BindingStatusEnum.BOUND.getCode()) { - throw new RuntimeException("设备已绑定"); + + QueryWrapper bindRecordQueryWrapper = new QueryWrapper<>(); + bindRecordQueryWrapper.eq("device_id", device.getId()); + bindRecordQueryWrapper.eq("binding_user_id", userId); + AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper); + if (appDeviceBindRecord != null) { + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("device_id", device.getId()) + .eq("binding_user_id", userId) + .set("binding_user_id", userId) + .set("binding_time", new Date()); + return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper); + }else{ + AppDeviceBindRecord bindRecord = new AppDeviceBindRecord(); + bindRecord.setDeviceId(device.getId()); + bindRecord.setBindingUserId(userId); + bindRecord.setBindingTime(new Date()); + bindRecord.setCreateBy(userId); + appDeviceBindRecordMapper.insert(bindRecord); } + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); deviceUpdateWrapper.eq("id", device.getId()) .set("binding_status", BindingStatusEnum.BOUND.getCode()) @@ -159,10 +209,21 @@ public class AppDeviceBizService { } UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); deviceUpdateWrapper.eq("id", device.getId()) - .set("binding_status", BindingStatusEnum.UNBOUND.getCode()) .set("binding_user_id", null) + .set("binding_status", BindingStatusEnum.UNBOUND.getCode()) .set("binding_time", null); - return deviceMapper.update(null, deviceUpdateWrapper); + deviceMapper.update(null, deviceUpdateWrapper); + + Long userId = AppLoginHelper.getUserId(); + QueryWrapper bindRecordQueryWrapper = new QueryWrapper<>(); + bindRecordQueryWrapper.eq("device_id", device.getId()); + bindRecordQueryWrapper.eq("binding_user_id", userId); + AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper); + if (appDeviceBindRecord != null) { + return appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId()); + } + + return 1; } public AppDeviceDetailVo getInfo(Long id) { @@ -196,6 +257,42 @@ public class AppDeviceBizService { return vo; } + public static void main(String[] args) { + byte[] unitName = generateFixedBitmapData("富源晟科技", 120); + byte[] position = generateFixedBitmapData("研发", 120); + byte[] name = generateFixedBitmapData("张三", 120); + byte[] id = generateFixedBitmapData("123456", 120); +// int[] intUnitNames = Bitmap80x12Generator.convertHexToDecimal(unitName); +// int[] intPosition = Bitmap80x12Generator.convertHexToDecimal(position); +// int[] intNames = Bitmap80x12Generator.convertHexToDecimal(position); +// int[] intIds = Bitmap80x12Generator.convertHexToDecimal(position); +// Map map = new HashMap<>(); +// map.put("instruct", 2); +// System.out.println(JSON.toJSONString( map)); +// StringBuilder sb = new StringBuilder(); +// sb.append("[") +// buildStr(unitName, sb); +// System.out.println(sb.toString()); +// Object[] arr = new Object[]{2, Bitmap80x12Generator , Arrays.toString(name), Arrays.toString(id)}; + +// System.out.println(Arrays.deepToString(arr)); +// int[] a = new int[]{6,6,6,6,6,6}; + ArrayList intData = new ArrayList<>(); + intData.add(2); + buildArr(convertHexToDecimal(unitName),intData); + buildArr(convertHexToDecimal(position),intData); + buildArr(convertHexToDecimal(name),intData); + buildArr(convertHexToDecimal(id),intData); + intData.add(0); + intData.add(0); + intData.add(0); + intData.add(0); + Map map = new HashMap<>(); + map.put("instruct", intData); + System.out.println(JSON.toJSONString( map)); + } + + public boolean registerPersonInfo(AppPersonnelInfoBo bo) { Long deviceId = bo.getDeviceId(); Device deviceObj = deviceMapper.selectById(deviceId); @@ -206,18 +303,24 @@ public class AppDeviceBizService { .eq("device_id", deviceId); List appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw); // unitName,position,name,id - byte[] unitName = ReliableTextToBitmap.textToBitmapBytes(bo.getUnitName()); - byte[] position = ReliableTextToBitmap.textToBitmapBytes(bo.getPosition()); - byte[] name = ReliableTextToBitmap.textToBitmapBytes(bo.getName()); - byte[] id = ReliableTextToBitmap.textToBitmapBytes(bo.getCode()); - Map linkHashMap = new HashMap<>(); - linkHashMap.put("unitName",unitName); - linkHashMap.put("position",position); - linkHashMap.put("name",name); - linkHashMap.put("id",id); - String personnelInfo = JSON.toJSONString(linkHashMap); - mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+deviceObj.getDeviceImei(), 1 ,personnelInfo); - log.info("发送点阵数据到设备消息:{}", bo); + byte[] unitName = generateFixedBitmapData(bo.getUnitName(), 120); + byte[] position = generateFixedBitmapData(bo.getPosition(), 120); + byte[] name = generateFixedBitmapData(bo.getName(), 120); + byte[] id = generateFixedBitmapData(bo.getCode(), 120); + ArrayList intData = new ArrayList<>(); + intData.add(2); + buildArr(convertHexToDecimal(unitName),intData); + buildArr(convertHexToDecimal(position),intData); + buildArr(convertHexToDecimal(name),intData); + buildArr(convertHexToDecimal(id),intData); + intData.add(0); + intData.add(0); + intData.add(0); + intData.add(0); + Map map = new HashMap<>(); + map.put("instruct", intData); + mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+deviceObj.getDeviceImei(), 1 , JSON.toJSONString( map)); + log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+deviceObj.getDeviceImei(),bo); if(ObjectUtils.length(appPersonnelInfoVos) == 0){ AppPersonnelInfo appPersonnelInfo = MapstructUtils.convert(bo, AppPersonnelInfo.class); @@ -234,4 +337,44 @@ public class AppDeviceBizService { } + + public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) { + try { + Device device = deviceMapper.selectById(bo.getDeviceId()); + if(device == null){ + throw new ServiceException("设备不存在"); + } + MultipartFile file = bo.getFile(); + + byte[] largeData = ImageToCArrayConverter.convertImageToCArray(file.getInputStream(), 160, 80,25600); + System.out.println("长度:"+largeData.length); + + System.out.println("原始数据大小: " + largeData.length + " 字节"); + + int[] ints = convertHexToDecimal(largeData); + RedisUtils.setCacheObject("app_logo_data:"+device.getDeviceImei(), Arrays.toString(ints), Duration.ofSeconds(24*60*60L)); + + String data = RedisUtils.getCacheObject("app_logo_data:"+device.getDeviceImei()); + + byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data); + byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, 0, 512); + System.out.println("第0块数据大小: " + specificChunk.length + " 字节"); + System.out.println("第0块数据: " + Arrays.toString(specificChunk)); + + ArrayList intData = new ArrayList<>(); + intData.add(3); + intData.add(1); + ImageToCArrayConverter.buildArr(convertHexToDecimal(specificChunk),intData); + intData.add(0); + intData.add(0); + intData.add(0); + intData.add(0); + Map map = new HashMap<>(); + map.put("instruct", intData); + mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(), 1 , JSON.toJSONString(map)); + log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),JSON.toJSONString(map)); + } catch (Exception e){ + e.printStackTrace(); + } + } } diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/Bitmap80x12Generator.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/Bitmap80x12Generator.java new file mode 100644 index 00000000..5c6996eb --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/Bitmap80x12Generator.java @@ -0,0 +1,362 @@ +package com.fuyuanshen.common.core.utils; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 80*12像素点阵生成工具 + */ +public class Bitmap80x12Generator { + + public static void main(String[] args) throws IOException { + // 测试生成中文文本的点阵数据 + String text = "张三"; + byte[] bitmapData = generateFixedBitmapData(text, 120); +// System.out.println(Arrays.toString(bitmapData)); + int[] ints = convertHexToDecimal(bitmapData); + System.out.println(Arrays.toString(ints)); + // 生成预览图片 + byte[] bytes = convertDecimalToByteArray(ints); + BufferedImage image = convertByteArrayToImage(bytes, 12, 80); + ImageIO.write(image, "PNG", new File("D:\\bitmap_preview.png")); + System.out.println("成功生成预览图片: D:\\bitmap_preview.png"); + + // 打印十六进制数据 +// System.out.println("生成的点阵数据2:"); +// printHexData(bitmapData); +// int[] ints = convertHexToDecimal(bitmapData); + System.out.println("打印十进制无符号:"+Arrays.toString(ints)); +// printDecimalData(bitmapData); + + // 生成C文件 + generateCFile(bitmapData, "bitmap_data.c", "chinese_text"); + } + + /** + * 将十进制整数数组转换为字节数组 + * + * @param decimalArray 十进制整数数组(假设每个值都在0-255范围内) + * @return 字节数组 + */ + public static byte[] convertDecimalToByteArray(int[] decimalArray) { + if (decimalArray == null) { + return new byte[0]; + } + + byte[] byteArray = new byte[decimalArray.length]; + for (int i = 0; i < decimalArray.length; i++) { + // 确保值在0-255范围内,这是byte的无符号表示范围 + int value = decimalArray[i] & 0xFF; + byteArray[i] = (byte) value; + } + + return byteArray; + } + + /** + * 打印字节数组(以十进制形式显示) + * + * @param data 字节数组 + */ + public static void printByteArrayAsDecimal(byte[] data) { + System.out.println("字节数组(十进制显示):"); + for (int i = 0; i < data.length; i++) { + // 将字节转换为无符号十进制数显示 + int value = data[i] & 0xFF; + System.out.print(value); + + if (i < data.length - 1) { + System.out.print(", "); + if ((i + 1) % 12 == 0) { + System.out.println(); + } + } + } + System.out.println(); + } + + + /** + * 将十六进制字节数组转换为十进制整数数组 + * + * @param data 字节数组 + * @return 十进制整数数组 + */ + public static int[] convertHexToDecimal(byte[] data) { + if (data == null) { + return new int[0]; + } + + int[] decimalArray = new int[data.length]; + for (int i = 0; i < data.length; i++) { + // 将字节转换为无符号整数(十进制) + decimalArray[i] = data[i] & 0xFF; + } + + return decimalArray; + } + + /** + * 打印十进制数据 + * + * @param data 字节数组 + */ + public static void printDecimalData(byte[] data) { + int[] decimalArray = convertHexToDecimal(data); + + System.out.println("生成的十进制数据:"); + for (int i = 0; i < decimalArray.length; i++) { + System.out.print(decimalArray[i]); + + if (i < decimalArray.length - 1) { + System.out.print(", "); + if ((i + 1) % 12 == 0) { + System.out.println(); + } + } + } + System.out.println(); + } + + public static void buildArr(int[] data,List intData){ + for (int datum : data) { + intData.add(datum); + } + } + + /** + * 生成固定长度的点阵数据 + * + * @param text 要转换的文本 + * @param fixedLength 固定长度(字节) + * @return 固定长度的点阵数据 + */ + public static byte[] generateFixedBitmapData(String text, int fixedLength) { + if (text == null || text.isEmpty()) { + return new byte[fixedLength]; + } + + // 创建80*12像素的图像 + Font font = new Font("宋体", Font.PLAIN, 12); + BufferedImage image = createTextImage(text, font, 80, 12); + + // 提取点阵数据 + byte[] rawData = extractBitmapData(image); +// System.out.println("生成的点阵数据1:"); +// System.out.println(Arrays.toString(rawData)); + + // 调整到固定长度 + byte[] result = new byte[fixedLength]; + int copyLength = Math.min(rawData.length, fixedLength); + System.arraycopy(rawData, 0, result, 0, copyLength); + // 剩余部分自动初始化为0 + + return result; + } + + /** + * 创建文本图像 + */ + private static BufferedImage createTextImage(String text, Font font, int width, int height) { + // 创建图像 + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + + // 设置白色背景 + g.setColor(Color.WHITE); + g.fillRect(0, 0, width, height); + + // 设置黑色文本 + g.setColor(Color.BLACK); + g.setFont(font); + + // 关闭抗锯齿 + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + g.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + + // 获取字体度量 + FontMetrics metrics = g.getFontMetrics(); + + // 计算文本绘制位置(居中) + int textWidth = metrics.stringWidth(text); +// int x = Math.max(0, (width - textWidth) / 2); // 水平居中 + // 左对齐 + int x = 0; + int y = (height - metrics.getHeight()) / 2 + metrics.getAscent(); // 垂直居中 + + // 绘制文本 + g.drawString(text, x, y); + + g.dispose(); + return image; + } + + /** + * 提取点阵数据 - 从左到右,从上到下扫描 + */ + private static byte[] extractBitmapData(BufferedImage image) { + int width = image.getWidth(); + int height = image.getHeight(); + + List byteList = new ArrayList<>(); + int currentByte = 0; + int bitCount = 0; + + // 从上到下,从左到右扫描 + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // 获取像素颜色 + Color color = new Color(image.getRGB(x, y)); + + // 判断是否为黑色(阈值处理) + int gray = (color.getRed() + color.getGreen() + color.getBlue()) / 3; + boolean isBlack = gray < 128; + + // 高位优先打包 + currentByte = (currentByte << 1) | (isBlack ? 1 : 0); + bitCount++; + + if (bitCount == 8) { + byteList.add((byte) currentByte); + currentByte = 0; + bitCount = 0; + } + } + } + + // 处理最后不满8位的部分 + if (bitCount > 0) { + currentByte <<= (8 - bitCount); + byteList.add((byte) currentByte); + } + + return byteListToArray(byteList); + } + + private static byte[] byteListToArray(List byteList) { + byte[] result = new byte[byteList.size()]; + for (int i = 0; i < byteList.size(); i++) { + result[i] = byteList.get(i); + } + return result; + } + + /** + * 字节数组转图像 + */ + public static BufferedImage convertByteArrayToImage(byte[] data, int height, int width) { + if (data == null || data.length == 0) { + return new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); + } + + // 创建RGB图像 + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + + // 设置白色背景 + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + image.setRGB(x, y, Color.WHITE.getRGB()); + } + } + + // 从左到右,从上到下设置像素点 + int bitIndex = 0; + for (int i = 0; i < data.length; i++) { + int value = data[i] & 0xFF; + for (int bit = 7; bit >= 0; bit--) { // 高位在前 + boolean isBlack = ((value >> bit) & 1) == 1; + if (isBlack) { + int x = bitIndex % width; + int y = bitIndex / width; + if (x < width && y < height) { // 确保不越界 + image.setRGB(x, y, Color.BLACK.getRGB()); + } + } + bitIndex++; + + // 如果已经处理完所有像素,则退出 + if (bitIndex >= width * height) { + return image; + } + } + } + + return image; + } + + public static String convertToCArrayString(byte[] data, String arrayName) { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("// %s: %d 字节\n", arrayName, data.length)); + sb.append(String.format("const uint8_t %s[] = {\n ", arrayName)); + + for (int i = 0; i < data.length; i++) { + sb.append(String.format("0x%02X", data[i] & 0xFF)); + + if (i < data.length - 1) { + sb.append(", "); + // 每12个元素换行 + if ((i + 1) % 12 == 0) { + sb.append("\n "); + } + } + } + + sb.append("\n};"); + return sb.toString(); + } + /** + * 打印十六进制数据 + */ + private static void printHexData(byte[] data) { + for (int i = 0; i < data.length; i++) { + int value = data[i] & 0xFF; + System.out.printf("0x%02X", value); + + if (i < data.length - 1) { + System.out.print(", "); + if ((i + 1) % 12 == 0) System.out.println(); + } + } + System.out.println(); + } + + /** + * 生成C文件 + */ + public static void generateCFile(byte[] data, String filename, String arrayName) throws IOException { + try (FileWriter writer = new FileWriter(filename)) { + writer.write("/**\n"); + writer.write(" * 80*12点阵显示数据\n"); + writer.write(" * 数据大小: " + data.length + " 字节\n"); + writer.write(" * 分辨率: 80*12 像素\n"); + writer.write(" */\n\n"); + writer.write("#include \n\n"); + + writer.write(String.format("// %s: %d 字节, 80*12 像素\n", arrayName, data.length)); + writer.write(String.format("const uint8_t %s[] = {\n ", arrayName)); + writeByteArray(writer, data); + writer.write("\n};\n"); + } + } + + private static void writeByteArray(FileWriter writer, byte[] data) throws IOException { + for (int i = 0; i < data.length; i++) { + int value = data[i] & 0xFF; + writer.write(String.format("0x%02X", value)); + + if (i < data.length - 1) { + writer.write(", "); + if ((i + 1) % 12 == 0) writer.write("\n "); + } + } + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverter.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverter.java new file mode 100644 index 00000000..0ad17823 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverter.java @@ -0,0 +1,313 @@ +package com.fuyuanshen.common.core.utils; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +public class ImageToCArrayConverter { + +/* public static void main(String[] args) { + try { + byte[] imageData = convertImageToCArray("E:\\workspace\\6170_强光_160_80_2.jpg", 160, 80,25600); + System.out.println("长度:"+imageData.length); +// int[] ints =convertHexToDecimal(imageData); +// System.out.println("Image data: " + Arrays.toString(ints)); +// writeFile("E:\\workspace\\output.c", imageData,160,80); +// System.out.println("转换成功!"); + ArrayList intData = new ArrayList<>(); + intData.add(2); + buildArr(convertHexToDecimal(imageData),intData); + intData.add(0); + intData.add(0); + intData.add(0); + intData.add(0); + Map map = new HashMap<>(); + map.put("instruct", intData); + System.out.println(JSON.toJSONString( map)); + } catch (IOException e) { + System.err.println("转换失败: " + e.getMessage()); + } + }*/ + + public static void main(String[] args) throws IOException { + + byte[] largeData = convertImageToCArray("E:\\workspace\\6170_强光_160_80_2.jpg", 160, 80,25600); + System.out.println("长度:"+largeData.length); + + System.out.println("原始数据大小: " + largeData.length + " 字节"); + + // 将25600字节的数据分割成512字节的块 +// List chunks = splitByteArrayIntoChunks(largeData, 512); +// printChunkInfo(chunks); +// +// // 打印前几块的数据示例 +// System.out.println("\n前3块数据示例(十进制显示):"); +// for (int i = 0; i < Math.min(50, chunks.size()); i++) { +// System.out.println("块 " + i + ":"); +// int[] ints = convertHexToDecimal(chunks.get(i)); +// System.out.println(Arrays.toString(ints)); +// } + + // 示例:获取特定块的数据 + byte[] specificChunk = getChunk(largeData, 5, 512); // 获取第6块(索引5) + System.out.println("第6块数据大小: " + specificChunk.length + " 字节"); + + // 生成预览图片 +// BufferedImage image = convertByteArrayToImage(bitmapData, 12, 80); +// ImageIO.write(image, "PNG", new File("D:\\bitmap_preview.png")); +// System.out.println("成功生成预览图片: D:\\bitmap_preview.png"); +// +// // 生成C文件 +// generateCFile(bitmapData, "bitmap_data.c", "chinese_text"); + } + /** + * 获取指定块的数据 + * + * @param data 原始字节数组 + * @param chunkIndex 块索引(从0开始) + * @param chunkSize 每块大小 + * @return 指定块的字节数组,如果索引无效则返回空数组 + */ + public static byte[] getChunk(byte[] data, int chunkIndex, int chunkSize) { + if (data == null || chunkSize <= 0 || chunkIndex < 0) { + return new byte[0]; + } + + int start = chunkIndex * chunkSize; + if (start >= data.length) { + return new byte[0]; // 索引超出范围 + } + + int end = Math.min(start + chunkSize, data.length); + int length = end - start; + + byte[] chunk = new byte[length]; + System.arraycopy(data, start, chunk, 0, length); + return chunk; + } + + public static void buildArr(int[] data,List intData){ + for (int datum : data) { + intData.add(datum); + } + } + + /** + * 打印分块信息 + * + * @param chunks 分块后的字节数组列表 + */ + public static void printChunkInfo(List chunks) { + System.out.println("总共分割成 " + chunks.size() + " 块"); + for (int i = 0; i < chunks.size(); i++) { + System.out.println("块 " + i + ": " + chunks.get(i).length + " 字节"); + } + } + /** + * 将大字节数组分割成固定大小的块 + * + * @param data 原始字节数组 + * @param chunkSize 每块大小(字节数) + * @return 分割后的字节数组列表 + */ + public static List splitByteArrayIntoChunks(byte[] data, int chunkSize) { + if (data == null || data.length == 0 || chunkSize <= 0) { + return new ArrayList<>(); + } + + List chunks = new ArrayList<>(); + int totalChunks = (int) Math.ceil((double) data.length / chunkSize); + + for (int i = 0; i < totalChunks; i++) { + int start = i * chunkSize; + int end = Math.min(start + chunkSize, data.length); + int length = end - start; + + byte[] chunk = new byte[length]; + System.arraycopy(data, start, chunk, 0, length); + chunks.add(chunk); + } + + return chunks; + } + + public static int[] convertHexToDecimal(byte[] data) { + if (data == null) { + return new int[0]; + } + + int[] decimalArray = new int[data.length]; + for (int i = 0; i < data.length; i++) { + // 将字节转换为无符号整数(十进制) + decimalArray[i] = data[i] & 0xFF; + } + + return decimalArray; + } + + public static byte[] convertImageToCArray(InputStream inputStream, + int width, int height, int fixedLength) throws IOException { + // 读取原始图片 + BufferedImage originalImage = ImageIO.read(inputStream); + + // 调整图片尺寸 + BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + resizedImage.getGraphics().drawImage( + originalImage, 0, 0, width, height, null); + + // 转换像素数据为RGB565格式(高位在前) + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int rgb = resizedImage.getRGB(x, y); + + // 提取RGB分量 + int r = (rgb >> 16) & 0xFF; + int g = (rgb >> 8) & 0xFF; + int b = rgb & 0xFF; + + // 转换为RGB565(5位红,6位绿,5位蓝) + int r5 = (r >> 3) & 0x1F; + int g6 = (g >> 2) & 0x3F; + int b5 = (b >> 3) & 0x1F; + + // 组合为16位值 + int rgb565 = (r5 << 11) | (g6 << 5) | b5; + + // 高位在前(大端序)写入字节 + byteStream.write((rgb565 >> 8) & 0xFF); // 高字节 + byteStream.write(rgb565 & 0xFF); // 低字节 + } + } + // 调整到固定长度 + byte[] rawData = byteStream.toByteArray(); + byte[] result = new byte[fixedLength]; + int copyLength = Math.min(rawData.length, fixedLength); + System.arraycopy(rawData, 0, result, 0, copyLength); + return result; + } + + public static byte[] convertImageToCArray(String inputPath, + int width, int height, int fixedLength) throws IOException { + // 读取原始图片 + BufferedImage originalImage = ImageIO.read(new File(inputPath)); + + // 调整图片尺寸 + BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + resizedImage.getGraphics().drawImage( + originalImage, 0, 0, width, height, null); + + // 转换像素数据为RGB565格式(高位在前) + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int rgb = resizedImage.getRGB(x, y); + + // 提取RGB分量 + int r = (rgb >> 16) & 0xFF; + int g = (rgb >> 8) & 0xFF; + int b = rgb & 0xFF; + + // 转换为RGB565(5位红,6位绿,5位蓝) + int r5 = (r >> 3) & 0x1F; + int g6 = (g >> 2) & 0x3F; + int b5 = (b >> 3) & 0x1F; + + // 组合为16位值 + int rgb565 = (r5 << 11) | (g6 << 5) | b5; + + // 高位在前(大端序)写入字节 + byteStream.write((rgb565 >> 8) & 0xFF); // 高字节 + byteStream.write(rgb565 & 0xFF); // 低字节 + } + } + // 调整到固定长度 + byte[] rawData = byteStream.toByteArray(); + byte[] result = new byte[fixedLength]; + int copyLength = Math.min(rawData.length, fixedLength); + System.arraycopy(rawData, 0, result, 0, copyLength); + return result; + } + + private static void writeFile(String outputPath, byte[] imageData,int width, int height) throws IOException { + // 生成C语言数组文件 + try (FileOutputStream fos = new FileOutputStream(outputPath)) { + // 写入注释行(包含尺寸信息) + String header = String.format("/* 0X10,0X10,0X00,0X%02X,0X00,0X%02X,0X01,0X1B, */\n", + width, height); + fos.write(header.getBytes()); + + // 写入数组声明 + fos.write("const unsigned char gImage_data[] = {\n".getBytes()); + + // 写入数据(每行16个字节) + for (int i = 0; i < imageData.length; i++) { + // 写入0X前缀 + fos.write(("0X" + String.format("%02X", imageData[i] & 0xFF)).getBytes()); + + // 添加逗号(最后一个除外) + if (i < imageData.length - 1) { + fos.write(','); + } + + // 换行和缩进 + if ((i + 1) % 16 == 0) { + fos.write('\n'); + } else { + fos.write(' '); + } + } + + // 写入数组结尾 + fos.write("\n};\n".getBytes()); + } + } + + /** + * 将字节字符串转换为字节数组 + * + * @param byteString 字节字符串,格式如 "[12, 45, 67, ...]" + * @return 字节数组 + */ + public static byte[] convertStringToByteArray(String byteString) { + if (byteString == null || byteString.isEmpty()) { + return new byte[0]; + } + + try { + // 移除方括号 + String content = byteString.trim(); + if (content.startsWith("[")) { + content = content.substring(1); + } + if (content.endsWith("]")) { + content = content.substring(0, content.length() - 1); + } + + // 按逗号分割 + String[] byteValues = content.split(","); + byte[] result = new byte[byteValues.length]; + + // 转换每个值 + for (int i = 0; i < byteValues.length; i++) { + String value = byteValues[i].trim(); + // 处理可能的进制前缀 + if (value.startsWith("0x") || value.startsWith("0X")) { + // 十六进制 + result[i] = (byte) Integer.parseInt(value.substring(2), 16); + } else { + // 十进制 + int intValue = Integer.parseInt(value); + result[i] = (byte) intValue; + } + } + + return result; + } catch (NumberFormatException e) { + System.err.println("解析字节字符串时出错: " + e.getMessage()); + return new byte[0]; + } + } +} \ No newline at end of file diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverterUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverterUtils.java deleted file mode 100644 index 598db1e5..00000000 --- a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverterUtils.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.fuyuanshen.common.core.utils; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; - -public class ImageToCArrayConverterUtils { - - public static void main(String[] args) { - try { - convertImageToCArray("E:\\workspace\\6170_强光_160_80_2.jpg", "E:\\workspace\\output.c", 160, 80); - System.out.println("转换成功!"); - } catch (IOException e) { - System.err.println("转换失败: " + e.getMessage()); - } - } - - public static void convertImageToCArray(String inputPath, String outputPath, - int width, int height) throws IOException { - // 读取原始图片 - BufferedImage originalImage = ImageIO.read(new File(inputPath)); - - // 调整图片尺寸 - BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - resizedImage.getGraphics().drawImage( - originalImage, 0, 0, width, height, null); - - // 转换像素数据为RGB565格式(高位在前) - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int rgb = resizedImage.getRGB(x, y); - - // 提取RGB分量 - int r = (rgb >> 16) & 0xFF; - int g = (rgb >> 8) & 0xFF; - int b = rgb & 0xFF; - - // 转换为RGB565(5位红,6位绿,5位蓝) - int r5 = (r >> 3) & 0x1F; - int g6 = (g >> 2) & 0x3F; - int b5 = (b >> 3) & 0x1F; - - // 组合为16位值 - int rgb565 = (r5 << 11) | (g6 << 5) | b5; - - // 高位在前(大端序)写入字节 - byteStream.write((rgb565 >> 8) & 0xFF); // 高字节 - byteStream.write(rgb565 & 0xFF); // 低字节 - } - } - - byte[] imageData = byteStream.toByteArray(); - - // 生成C语言数组文件 - try (FileOutputStream fos = new FileOutputStream(outputPath)) { - // 写入注释行(包含尺寸信息) - String header = String.format("/* 0X10,0X10,0X00,0X%02X,0X00,0X%02X,0X01,0X1B, */\n", - width, height); - fos.write(header.getBytes()); - - // 写入数组声明 - fos.write("const unsigned char gImage_data[] = {\n".getBytes()); - - // 写入数据(每行16个字节) - for (int i = 0; i < imageData.length; i++) { - // 写入0X前缀 - fos.write(("0X" + String.format("%02X", imageData[i] & 0xFF)).getBytes()); - - // 添加逗号(最后一个除外) - if (i < imageData.length - 1) { - fos.write(','); - } - - // 换行和缩进 - if ((i + 1) % 16 == 0) { - fos.write('\n'); - } else { - fos.write(' '); - } - } - - // 写入数组结尾 - fos.write("\n};\n".getBytes()); - } - } -} \ No newline at end of file diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/AppDeviceBindRecordController.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/AppDeviceBindRecordController.java new file mode 100644 index 00000000..fc5c9cd1 --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/controller/AppDeviceBindRecordController.java @@ -0,0 +1,105 @@ +package com.fuyuanshen.app.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.app.domain.vo.AppDeviceBindRecordVo; +import com.fuyuanshen.app.domain.bo.AppDeviceBindRecordBo; +import com.fuyuanshen.app.service.IAppDeviceBindRecordService; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; + +/** + * 设备绑定关系 + * + * @author Lion Li + * @date 2025-07-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/app/deviceBindRecord") +public class AppDeviceBindRecordController extends BaseController { + + private final IAppDeviceBindRecordService appDeviceBindRecordService; + + /** + * 查询设备绑定关系列表 + */ + @SaCheckPermission("app:deviceBindRecord:list") + @GetMapping("/list") + public TableDataInfo list(AppDeviceBindRecordBo bo, PageQuery pageQuery) { + return appDeviceBindRecordService.queryPageList(bo, pageQuery); + } + + /** + * 导出设备绑定关系列表 + */ + @SaCheckPermission("app:deviceBindRecord:export") + @Log(title = "设备绑定关系", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(AppDeviceBindRecordBo bo, HttpServletResponse response) { + List list = appDeviceBindRecordService.queryList(bo); + ExcelUtil.exportExcel(list, "设备绑定关系", AppDeviceBindRecordVo.class, response); + } + + /** + * 获取设备绑定关系详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("app:deviceBindRecord:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(appDeviceBindRecordService.queryById(id)); + } + + /** + * 新增设备绑定关系 + */ + @SaCheckPermission("app:deviceBindRecord:add") + @Log(title = "设备绑定关系", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody AppDeviceBindRecordBo bo) { + return toAjax(appDeviceBindRecordService.insertByBo(bo)); + } + + /** + * 修改设备绑定关系 + */ + @SaCheckPermission("app:deviceBindRecord:edit") + @Log(title = "设备绑定关系", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody AppDeviceBindRecordBo bo) { + return toAjax(appDeviceBindRecordService.updateByBo(bo)); + } + + /** + * 删除设备绑定关系 + * + * @param ids 主键串 + */ + @SaCheckPermission("app:deviceBindRecord:remove") + @Log(title = "设备绑定关系", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(appDeviceBindRecordService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/AppDeviceBindRecord.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/AppDeviceBindRecord.java new file mode 100644 index 00000000..d6019213 --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/AppDeviceBindRecord.java @@ -0,0 +1,54 @@ +package com.fuyuanshen.app.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 设备绑定关系对象 app_device_bind_record + * + * @author Lion Li + * @date 2025-07-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("app_device_bind_record") +public class AppDeviceBindRecord extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id") + private Long id; + + /** + * 设备id + */ + private Long deviceId; + + /** + * 绑定用户id + */ + private Long bindingUserId; + + /** + * 备注 + */ + private String remark; + + /** + * 绑定时间 + */ + private Date bindingTime; + + +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppDeviceBindRecordBo.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppDeviceBindRecordBo.java new file mode 100644 index 00000000..4742933b --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/bo/AppDeviceBindRecordBo.java @@ -0,0 +1,51 @@ +package com.fuyuanshen.app.domain.bo; + +import com.fuyuanshen.app.domain.AppDeviceBindRecord; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 设备绑定关系业务对象 app_device_bind_record + * + * @author Lion Li + * @date 2025-07-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = AppDeviceBindRecord.class, reverseConvertGenerate = false) +public class AppDeviceBindRecordBo extends BaseEntity { + + /** + * 主键ID + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 设备id + */ + private Long deviceId; + + /** + * 绑定用户id + */ + private Long bindingUserId; + + /** + * 备注 + */ + private String remark; + + /** + * 绑定时间 + */ + private Date bindingTime; + + +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceBindRecordVo.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceBindRecordVo.java new file mode 100644 index 00000000..206b3f9e --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/domain/vo/AppDeviceBindRecordVo.java @@ -0,0 +1,64 @@ +package com.fuyuanshen.app.domain.vo; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fuyuanshen.app.domain.AppDeviceBindRecord; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 设备绑定关系视图对象 app_device_bind_record + * + * @author Lion Li + * @date 2025-07-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = AppDeviceBindRecord.class) +public class AppDeviceBindRecordVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @ExcelProperty(value = "主键ID") + private Long id; + + /** + * 设备id + */ + @ExcelProperty(value = "设备id") + private Long deviceId; + + /** + * 绑定用户id + */ + @ExcelProperty(value = "绑定用户id") + private Long bindingUserId; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 绑定时间 + */ + @ExcelProperty(value = "绑定时间") + private Date bindingTime; + + +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppDeviceBindRecordMapper.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppDeviceBindRecordMapper.java new file mode 100644 index 00000000..2fa66a8f --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/mapper/AppDeviceBindRecordMapper.java @@ -0,0 +1,15 @@ +package com.fuyuanshen.app.mapper; + +import com.fuyuanshen.app.domain.AppDeviceBindRecord; +import com.fuyuanshen.app.domain.vo.AppDeviceBindRecordVo; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 设备绑定关系Mapper接口 + * + * @author Lion Li + * @date 2025-07-28 + */ +public interface AppDeviceBindRecordMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/IAppDeviceBindRecordService.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/IAppDeviceBindRecordService.java new file mode 100644 index 00000000..2d3a2391 --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/IAppDeviceBindRecordService.java @@ -0,0 +1,68 @@ +package com.fuyuanshen.app.service; + +import com.fuyuanshen.app.domain.vo.AppDeviceBindRecordVo; +import com.fuyuanshen.app.domain.bo.AppDeviceBindRecordBo; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 设备绑定关系Service接口 + * + * @author Lion Li + * @date 2025-07-28 + */ +public interface IAppDeviceBindRecordService { + + /** + * 查询设备绑定关系 + * + * @param id 主键 + * @return 设备绑定关系 + */ + AppDeviceBindRecordVo queryById(Long id); + + /** + * 分页查询设备绑定关系列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 设备绑定关系分页列表 + */ + TableDataInfo queryPageList(AppDeviceBindRecordBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的设备绑定关系列表 + * + * @param bo 查询条件 + * @return 设备绑定关系列表 + */ + List queryList(AppDeviceBindRecordBo bo); + + /** + * 新增设备绑定关系 + * + * @param bo 设备绑定关系 + * @return 是否新增成功 + */ + Boolean insertByBo(AppDeviceBindRecordBo bo); + + /** + * 修改设备绑定关系 + * + * @param bo 设备绑定关系 + * @return 是否修改成功 + */ + Boolean updateByBo(AppDeviceBindRecordBo bo); + + /** + * 校验并批量删除设备绑定关系信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/impl/AppDeviceBindRecordServiceImpl.java b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/impl/AppDeviceBindRecordServiceImpl.java new file mode 100644 index 00000000..59c9fd9a --- /dev/null +++ b/fys-modules/fys-app/src/main/java/com/fuyuanshen/app/service/impl/AppDeviceBindRecordServiceImpl.java @@ -0,0 +1,133 @@ +package com.fuyuanshen.app.service.impl; + +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.fuyuanshen.app.domain.bo.AppDeviceBindRecordBo; +import com.fuyuanshen.app.domain.vo.AppDeviceBindRecordVo; +import com.fuyuanshen.app.domain.AppDeviceBindRecord; +import com.fuyuanshen.app.mapper.AppDeviceBindRecordMapper; +import com.fuyuanshen.app.service.IAppDeviceBindRecordService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 设备绑定关系Service业务层处理 + * + * @author Lion Li + * @date 2025-07-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class AppDeviceBindRecordServiceImpl implements IAppDeviceBindRecordService { + + private final AppDeviceBindRecordMapper baseMapper; + + /** + * 查询设备绑定关系 + * + * @param id 主键 + * @return 设备绑定关系 + */ + @Override + public AppDeviceBindRecordVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 分页查询设备绑定关系列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 设备绑定关系分页列表 + */ + @Override + public TableDataInfo queryPageList(AppDeviceBindRecordBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的设备绑定关系列表 + * + * @param bo 查询条件 + * @return 设备绑定关系列表 + */ + @Override + public List queryList(AppDeviceBindRecordBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(AppDeviceBindRecordBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(AppDeviceBindRecord::getId); + lqw.eq(bo.getDeviceId() != null, AppDeviceBindRecord::getDeviceId, bo.getDeviceId()); + lqw.eq(bo.getBindingUserId() != null, AppDeviceBindRecord::getBindingUserId, bo.getBindingUserId()); + lqw.eq(bo.getBindingTime() != null, AppDeviceBindRecord::getBindingTime, bo.getBindingTime()); + return lqw; + } + + /** + * 新增设备绑定关系 + * + * @param bo 设备绑定关系 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(AppDeviceBindRecordBo bo) { + AppDeviceBindRecord add = MapstructUtils.convert(bo, AppDeviceBindRecord.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改设备绑定关系 + * + * @param bo 设备绑定关系 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(AppDeviceBindRecordBo bo) { + AppDeviceBindRecord update = MapstructUtils.convert(bo, AppDeviceBindRecord.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(AppDeviceBindRecord entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除设备绑定关系信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppDeviceBindRecordMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppDeviceBindRecordMapper.xml new file mode 100644 index 00000000..a523cece --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppDeviceBindRecordMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceService.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceService.java index 7d18d1e9..22d738d3 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceService.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceService.java @@ -114,4 +114,5 @@ public interface DeviceService extends IService { * @return */ int webUnBindDevice(Long id); + } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/Bitmap80x12Generator.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/Bitmap80x12Generator.java new file mode 100644 index 00000000..2262e10c --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/Bitmap80x12Generator.java @@ -0,0 +1,362 @@ +//package com.fuyuanshen.equipment.utils.c; +// +//import javax.imageio.ImageIO; +//import java.awt.*; +//import java.awt.image.BufferedImage; +//import java.io.File; +//import java.io.FileWriter; +//import java.io.IOException; +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.List; +// +///** +// * 80*12像素点阵生成工具 +// */ +//public class Bitmap80x12Generator { +// +// public static void main(String[] args) throws IOException { +// // 测试生成中文文本的点阵数据 +// String text = "张三"; +// byte[] bitmapData = generateFixedBitmapData(text, 120); +//// System.out.println(Arrays.toString(bitmapData)); +// int[] ints = convertHexToDecimal(bitmapData); +// System.out.println(Arrays.toString(ints)); +// // 生成预览图片 +// byte[] bytes = convertDecimalToByteArray(ints); +// BufferedImage image = convertByteArrayToImage(bytes, 12, 80); +// ImageIO.write(image, "PNG", new File("D:\\bitmap_preview.png")); +// System.out.println("成功生成预览图片: D:\\bitmap_preview.png"); +// +// // 打印十六进制数据 +//// System.out.println("生成的点阵数据2:"); +//// printHexData(bitmapData); +//// int[] ints = convertHexToDecimal(bitmapData); +// System.out.println("打印十进制无符号:"+Arrays.toString(ints)); +//// printDecimalData(bitmapData); +// +// // 生成C文件 +// generateCFile(bitmapData, "bitmap_data.c", "chinese_text"); +// } +// +// /** +// * 将十进制整数数组转换为字节数组 +// * +// * @param decimalArray 十进制整数数组(假设每个值都在0-255范围内) +// * @return 字节数组 +// */ +// public static byte[] convertDecimalToByteArray(int[] decimalArray) { +// if (decimalArray == null) { +// return new byte[0]; +// } +// +// byte[] byteArray = new byte[decimalArray.length]; +// for (int i = 0; i < decimalArray.length; i++) { +// // 确保值在0-255范围内,这是byte的无符号表示范围 +// int value = decimalArray[i] & 0xFF; +// byteArray[i] = (byte) value; +// } +// +// return byteArray; +// } +// +// /** +// * 打印字节数组(以十进制形式显示) +// * +// * @param data 字节数组 +// */ +// public static void printByteArrayAsDecimal(byte[] data) { +// System.out.println("字节数组(十进制显示):"); +// for (int i = 0; i < data.length; i++) { +// // 将字节转换为无符号十进制数显示 +// int value = data[i] & 0xFF; +// System.out.print(value); +// +// if (i < data.length - 1) { +// System.out.print(", "); +// if ((i + 1) % 12 == 0) { +// System.out.println(); +// } +// } +// } +// System.out.println(); +// } +// +// +// /** +// * 将十六进制字节数组转换为十进制整数数组 +// * +// * @param data 字节数组 +// * @return 十进制整数数组 +// */ +// public static int[] convertHexToDecimal(byte[] data) { +// if (data == null) { +// return new int[0]; +// } +// +// int[] decimalArray = new int[data.length]; +// for (int i = 0; i < data.length; i++) { +// // 将字节转换为无符号整数(十进制) +// decimalArray[i] = data[i] & 0xFF; +// } +// +// return decimalArray; +// } +// +// /** +// * 打印十进制数据 +// * +// * @param data 字节数组 +// */ +// public static void printDecimalData(byte[] data) { +// int[] decimalArray = convertHexToDecimal(data); +// +// System.out.println("生成的十进制数据:"); +// for (int i = 0; i < decimalArray.length; i++) { +// System.out.print(decimalArray[i]); +// +// if (i < decimalArray.length - 1) { +// System.out.print(", "); +// if ((i + 1) % 12 == 0) { +// System.out.println(); +// } +// } +// } +// System.out.println(); +// } +// +// public static void buildArr(int[] data,List intData){ +// for (int datum : data) { +// intData.add(datum); +// } +// } +// +// /** +// * 生成固定长度的点阵数据 +// * +// * @param text 要转换的文本 +// * @param fixedLength 固定长度(字节) +// * @return 固定长度的点阵数据 +// */ +// public static byte[] generateFixedBitmapData(String text, int fixedLength) { +// if (text == null || text.isEmpty()) { +// return new byte[fixedLength]; +// } +// +// // 创建80*12像素的图像 +// Font font = new Font("宋体", Font.PLAIN, 12); +// BufferedImage image = createTextImage(text, font, 80, 12); +// +// // 提取点阵数据 +// byte[] rawData = extractBitmapData(image); +//// System.out.println("生成的点阵数据1:"); +//// System.out.println(Arrays.toString(rawData)); +// +// // 调整到固定长度 +// byte[] result = new byte[fixedLength]; +// int copyLength = Math.min(rawData.length, fixedLength); +// System.arraycopy(rawData, 0, result, 0, copyLength); +// // 剩余部分自动初始化为0 +// +// return result; +// } +// +// /** +// * 创建文本图像 +// */ +// private static BufferedImage createTextImage(String text, Font font, int width, int height) { +// // 创建图像 +// BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); +// Graphics2D g = image.createGraphics(); +// +// // 设置白色背景 +// g.setColor(Color.WHITE); +// g.fillRect(0, 0, width, height); +// +// // 设置黑色文本 +// g.setColor(Color.BLACK); +// g.setFont(font); +// +// // 关闭抗锯齿 +// g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, +// RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); +// g.setRenderingHint(RenderingHints.KEY_RENDERING, +// RenderingHints.VALUE_RENDER_QUALITY); +// +// // 获取字体度量 +// FontMetrics metrics = g.getFontMetrics(); +// +// // 计算文本绘制位置(居中) +// int textWidth = metrics.stringWidth(text); +//// int x = Math.max(0, (width - textWidth) / 2); // 水平居中 +// // 左对齐 +// int x = 0; +// int y = (height - metrics.getHeight()) / 2 + metrics.getAscent(); // 垂直居中 +// +// // 绘制文本 +// g.drawString(text, x, y); +// +// g.dispose(); +// return image; +// } +// +// /** +// * 提取点阵数据 - 从左到右,从上到下扫描 +// */ +// private static byte[] extractBitmapData(BufferedImage image) { +// int width = image.getWidth(); +// int height = image.getHeight(); +// +// List byteList = new ArrayList<>(); +// int currentByte = 0; +// int bitCount = 0; +// +// // 从上到下,从左到右扫描 +// for (int y = 0; y < height; y++) { +// for (int x = 0; x < width; x++) { +// // 获取像素颜色 +// Color color = new Color(image.getRGB(x, y)); +// +// // 判断是否为黑色(阈值处理) +// int gray = (color.getRed() + color.getGreen() + color.getBlue()) / 3; +// boolean isBlack = gray < 128; +// +// // 高位优先打包 +// currentByte = (currentByte << 1) | (isBlack ? 1 : 0); +// bitCount++; +// +// if (bitCount == 8) { +// byteList.add((byte) currentByte); +// currentByte = 0; +// bitCount = 0; +// } +// } +// } +// +// // 处理最后不满8位的部分 +// if (bitCount > 0) { +// currentByte <<= (8 - bitCount); +// byteList.add((byte) currentByte); +// } +// +// return byteListToArray(byteList); +// } +// +// private static byte[] byteListToArray(List byteList) { +// byte[] result = new byte[byteList.size()]; +// for (int i = 0; i < byteList.size(); i++) { +// result[i] = byteList.get(i); +// } +// return result; +// } +// +// /** +// * 字节数组转图像 +// */ +// public static BufferedImage convertByteArrayToImage(byte[] data, int height, int width) { +// if (data == null || data.length == 0) { +// return new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); +// } +// +// // 创建RGB图像 +// BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); +// +// // 设置白色背景 +// for (int y = 0; y < height; y++) { +// for (int x = 0; x < width; x++) { +// image.setRGB(x, y, Color.WHITE.getRGB()); +// } +// } +// +// // 从左到右,从上到下设置像素点 +// int bitIndex = 0; +// for (int i = 0; i < data.length; i++) { +// int value = data[i] & 0xFF; +// for (int bit = 7; bit >= 0; bit--) { // 高位在前 +// boolean isBlack = ((value >> bit) & 1) == 1; +// if (isBlack) { +// int x = bitIndex % width; +// int y = bitIndex / width; +// if (x < width && y < height) { // 确保不越界 +// image.setRGB(x, y, Color.BLACK.getRGB()); +// } +// } +// bitIndex++; +// +// // 如果已经处理完所有像素,则退出 +// if (bitIndex >= width * height) { +// return image; +// } +// } +// } +// +// return image; +// } +// +// public static String convertToCArrayString(byte[] data, String arrayName) { +// StringBuilder sb = new StringBuilder(); +// sb.append(String.format("// %s: %d 字节\n", arrayName, data.length)); +// sb.append(String.format("const uint8_t %s[] = {\n ", arrayName)); +// +// for (int i = 0; i < data.length; i++) { +// sb.append(String.format("0x%02X", data[i] & 0xFF)); +// +// if (i < data.length - 1) { +// sb.append(", "); +// // 每12个元素换行 +// if ((i + 1) % 12 == 0) { +// sb.append("\n "); +// } +// } +// } +// +// sb.append("\n};"); +// return sb.toString(); +// } +// /** +// * 打印十六进制数据 +// */ +// private static void printHexData(byte[] data) { +// for (int i = 0; i < data.length; i++) { +// int value = data[i] & 0xFF; +// System.out.printf("0x%02X", value); +// +// if (i < data.length - 1) { +// System.out.print(", "); +// if ((i + 1) % 12 == 0) System.out.println(); +// } +// } +// System.out.println(); +// } +// +// /** +// * 生成C文件 +// */ +// public static void generateCFile(byte[] data, String filename, String arrayName) throws IOException { +// try (FileWriter writer = new FileWriter(filename)) { +// writer.write("/**\n"); +// writer.write(" * 80*12点阵显示数据\n"); +// writer.write(" * 数据大小: " + data.length + " 字节\n"); +// writer.write(" * 分辨率: 80*12 像素\n"); +// writer.write(" */\n\n"); +// writer.write("#include \n\n"); +// +// writer.write(String.format("// %s: %d 字节, 80*12 像素\n", arrayName, data.length)); +// writer.write(String.format("const uint8_t %s[] = {\n ", arrayName)); +// writeByteArray(writer, data); +// writer.write("\n};\n"); +// } +// } +// +// private static void writeByteArray(FileWriter writer, byte[] data) throws IOException { +// for (int i = 0; i < data.length; i++) { +// int value = data[i] & 0xFF; +// writer.write(String.format("0x%02X", value)); +// +// if (i < data.length - 1) { +// writer.write(", "); +// if ((i + 1) % 12 == 0) writer.write("\n "); +// } +// } +// } +//} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/ImageToCArrayConverter.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/ImageToCArrayConverter.java new file mode 100644 index 00000000..3e007e5a --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/ImageToCArrayConverter.java @@ -0,0 +1,310 @@ +//package com.fuyuanshen.equipment.utils.c; +// +//import com.fuyuanshen.common.redis.utils.RedisUtils; +// +//import javax.imageio.ImageIO; +//import java.awt.image.BufferedImage; +//import java.io.*; +//import java.util.ArrayList; +//import java.util.List; +// +//public class ImageToCArrayConverter { +// +///* public static void main(String[] args) { +// try { +// byte[] imageData = convertImageToCArray("E:\\workspace\\6170_强光_160_80_2.jpg", 160, 80,25600); +// System.out.println("长度:"+imageData.length); +//// int[] ints =convertHexToDecimal(imageData); +//// System.out.println("Image data: " + Arrays.toString(ints)); +//// writeFile("E:\\workspace\\output.c", imageData,160,80); +//// System.out.println("转换成功!"); +// ArrayList intData = new ArrayList<>(); +// intData.add(2); +// buildArr(convertHexToDecimal(imageData),intData); +// intData.add(0); +// intData.add(0); +// intData.add(0); +// intData.add(0); +// Map map = new HashMap<>(); +// map.put("instruct", intData); +// System.out.println(JSON.toJSONString( map)); +// } catch (IOException e) { +// System.err.println("转换失败: " + e.getMessage()); +// } +// }*/ +// +// public static void main(String[] args) throws IOException { +// +// byte[] largeData = convertImageToCArray("E:\\workspace\\6170_强光_160_80_2.jpg", 160, 80,25600); +// System.out.println("长度:"+largeData.length); +// +// System.out.println("原始数据大小: " + largeData.length + " 字节"); +// +// // 将25600字节的数据分割成512字节的块 +//// List chunks = splitByteArrayIntoChunks(largeData, 512); +//// printChunkInfo(chunks); +//// +//// // 打印前几块的数据示例 +//// System.out.println("\n前3块数据示例(十进制显示):"); +//// for (int i = 0; i < Math.min(50, chunks.size()); i++) { +//// System.out.println("块 " + i + ":"); +//// int[] ints = convertHexToDecimal(chunks.get(i)); +//// System.out.println(Arrays.toString(ints)); +//// } +// +// RedisUtils.setCacheObject("app_logo_data", largeData); +// +// // 示例:获取特定块的数据 +// byte[] specificChunk = getChunk(largeData, 5, 512); // 获取第6块(索引5) +// System.out.println("第6块数据大小: " + specificChunk.length + " 字节"); +// +// // 生成预览图片 +//// BufferedImage image = convertByteArrayToImage(bitmapData, 12, 80); +//// ImageIO.write(image, "PNG", new File("D:\\bitmap_preview.png")); +//// System.out.println("成功生成预览图片: D:\\bitmap_preview.png"); +//// +//// // 生成C文件 +//// generateCFile(bitmapData, "bitmap_data.c", "chinese_text"); +// } +// /** +// * 获取指定块的数据 +// * +// * @param data 原始字节数组 +// * @param chunkIndex 块索引(从0开始) +// * @param chunkSize 每块大小 +// * @return 指定块的字节数组,如果索引无效则返回空数组 +// */ +// public static byte[] getChunk(byte[] data, int chunkIndex, int chunkSize) { +// if (data == null || chunkSize <= 0 || chunkIndex < 0) { +// return new byte[0]; +// } +// +// int start = chunkIndex * chunkSize; +// if (start >= data.length) { +// return new byte[0]; // 索引超出范围 +// } +// +// int end = Math.min(start + chunkSize, data.length); +// int length = end - start; +// +// byte[] chunk = new byte[length]; +// System.arraycopy(data, start, chunk, 0, length); +// return chunk; +// } +// /** +// * 打印分块信息 +// * +// * @param chunks 分块后的字节数组列表 +// */ +// public static void printChunkInfo(List chunks) { +// System.out.println("总共分割成 " + chunks.size() + " 块"); +// for (int i = 0; i < chunks.size(); i++) { +// System.out.println("块 " + i + ": " + chunks.get(i).length + " 字节"); +// } +// } +// /** +// * 将大字节数组分割成固定大小的块 +// * +// * @param data 原始字节数组 +// * @param chunkSize 每块大小(字节数) +// * @return 分割后的字节数组列表 +// */ +// public static List splitByteArrayIntoChunks(byte[] data, int chunkSize) { +// if (data == null || data.length == 0 || chunkSize <= 0) { +// return new ArrayList<>(); +// } +// +// List chunks = new ArrayList<>(); +// int totalChunks = (int) Math.ceil((double) data.length / chunkSize); +// +// for (int i = 0; i < totalChunks; i++) { +// int start = i * chunkSize; +// int end = Math.min(start + chunkSize, data.length); +// int length = end - start; +// +// byte[] chunk = new byte[length]; +// System.arraycopy(data, start, chunk, 0, length); +// chunks.add(chunk); +// } +// +// return chunks; +// } +// +// public static int[] convertHexToDecimal(byte[] data) { +// if (data == null) { +// return new int[0]; +// } +// +// int[] decimalArray = new int[data.length]; +// for (int i = 0; i < data.length; i++) { +// // 将字节转换为无符号整数(十进制) +// decimalArray[i] = data[i] & 0xFF; +// } +// +// return decimalArray; +// } +// +// public static byte[] convertImageToCArray(InputStream inputStream, +// int width, int height, int fixedLength) throws IOException { +// // 读取原始图片 +// BufferedImage originalImage = ImageIO.read(inputStream); +// +// // 调整图片尺寸 +// BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); +// resizedImage.getGraphics().drawImage( +// originalImage, 0, 0, width, height, null); +// +// // 转换像素数据为RGB565格式(高位在前) +// ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); +// for (int y = 0; y < height; y++) { +// for (int x = 0; x < width; x++) { +// int rgb = resizedImage.getRGB(x, y); +// +// // 提取RGB分量 +// int r = (rgb >> 16) & 0xFF; +// int g = (rgb >> 8) & 0xFF; +// int b = rgb & 0xFF; +// +// // 转换为RGB565(5位红,6位绿,5位蓝) +// int r5 = (r >> 3) & 0x1F; +// int g6 = (g >> 2) & 0x3F; +// int b5 = (b >> 3) & 0x1F; +// +// // 组合为16位值 +// int rgb565 = (r5 << 11) | (g6 << 5) | b5; +// +// // 高位在前(大端序)写入字节 +// byteStream.write((rgb565 >> 8) & 0xFF); // 高字节 +// byteStream.write(rgb565 & 0xFF); // 低字节 +// } +// } +// // 调整到固定长度 +// byte[] rawData = byteStream.toByteArray(); +// byte[] result = new byte[fixedLength]; +// int copyLength = Math.min(rawData.length, fixedLength); +// System.arraycopy(rawData, 0, result, 0, copyLength); +// return result; +// } +// +// public static byte[] convertImageToCArray(String inputPath, +// int width, int height, int fixedLength) throws IOException { +// // 读取原始图片 +// BufferedImage originalImage = ImageIO.read(new File(inputPath)); +// +// // 调整图片尺寸 +// BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); +// resizedImage.getGraphics().drawImage( +// originalImage, 0, 0, width, height, null); +// +// // 转换像素数据为RGB565格式(高位在前) +// ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); +// for (int y = 0; y < height; y++) { +// for (int x = 0; x < width; x++) { +// int rgb = resizedImage.getRGB(x, y); +// +// // 提取RGB分量 +// int r = (rgb >> 16) & 0xFF; +// int g = (rgb >> 8) & 0xFF; +// int b = rgb & 0xFF; +// +// // 转换为RGB565(5位红,6位绿,5位蓝) +// int r5 = (r >> 3) & 0x1F; +// int g6 = (g >> 2) & 0x3F; +// int b5 = (b >> 3) & 0x1F; +// +// // 组合为16位值 +// int rgb565 = (r5 << 11) | (g6 << 5) | b5; +// +// // 高位在前(大端序)写入字节 +// byteStream.write((rgb565 >> 8) & 0xFF); // 高字节 +// byteStream.write(rgb565 & 0xFF); // 低字节 +// } +// } +// // 调整到固定长度 +// byte[] rawData = byteStream.toByteArray(); +// byte[] result = new byte[fixedLength]; +// int copyLength = Math.min(rawData.length, fixedLength); +// System.arraycopy(rawData, 0, result, 0, copyLength); +// return result; +// } +// +// private static void writeFile(String outputPath, byte[] imageData,int width, int height) throws IOException { +// // 生成C语言数组文件 +// try (FileOutputStream fos = new FileOutputStream(outputPath)) { +// // 写入注释行(包含尺寸信息) +// String header = String.format("/* 0X10,0X10,0X00,0X%02X,0X00,0X%02X,0X01,0X1B, */\n", +// width, height); +// fos.write(header.getBytes()); +// +// // 写入数组声明 +// fos.write("const unsigned char gImage_data[] = {\n".getBytes()); +// +// // 写入数据(每行16个字节) +// for (int i = 0; i < imageData.length; i++) { +// // 写入0X前缀 +// fos.write(("0X" + String.format("%02X", imageData[i] & 0xFF)).getBytes()); +// +// // 添加逗号(最后一个除外) +// if (i < imageData.length - 1) { +// fos.write(','); +// } +// +// // 换行和缩进 +// if ((i + 1) % 16 == 0) { +// fos.write('\n'); +// } else { +// fos.write(' '); +// } +// } +// +// // 写入数组结尾 +// fos.write("\n};\n".getBytes()); +// } +// } +// +// /** +// * 将字节字符串转换为字节数组 +// * +// * @param byteString 字节字符串,格式如 "[12, 45, 67, ...]" +// * @return 字节数组 +// */ +// public static byte[] convertStringToByteArray(String byteString) { +// if (byteString == null || byteString.isEmpty()) { +// return new byte[0]; +// } +// +// try { +// // 移除方括号 +// String content = byteString.trim(); +// if (content.startsWith("[")) { +// content = content.substring(1); +// } +// if (content.endsWith("]")) { +// content = content.substring(0, content.length() - 1); +// } +// +// // 按逗号分割 +// String[] byteValues = content.split(","); +// byte[] result = new byte[byteValues.length]; +// +// // 转换每个值 +// for (int i = 0; i < byteValues.length; i++) { +// String value = byteValues[i].trim(); +// // 处理可能的进制前缀 +// if (value.startsWith("0x") || value.startsWith("0X")) { +// // 十六进制 +// result[i] = (byte) Integer.parseInt(value.substring(2), 16); +// } else { +// // 十进制 +// int intValue = Integer.parseInt(value); +// result[i] = (byte) intValue; +// } +// } +// +// return result; +// } catch (NumberFormatException e) { +// System.err.println("解析字节字符串时出错: " + e.getMessage()); +// return new byte[0]; +// } +// } +//} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/ReliableTextToBitmap.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/ReliableTextToBitmap.java index bde75c71..bd28dc98 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/ReliableTextToBitmap.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/utils/c/ReliableTextToBitmap.java @@ -7,6 +7,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -23,6 +24,11 @@ public class ReliableTextToBitmap { String name = "12李34四56"; byte[] unitBytes = textToBitmapBytes(unit); + for (int i = 0; i < unitBytes.length; i++) { + //打印byte转十进制 + System.out.printf("0x%02X", unitBytes[i]); + } +// System.out.println("单元数据: "+Arrays.toString(unitBytes)); byte[] deptBytes = textToBitmapBytes(department); byte[] nameBytes = textToBitmapBytes(name); diff --git a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml index 9a683adb..e54ae783 100644 --- a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml +++ b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml @@ -151,7 +151,8 @@ d.binding_time from device d inner join device_type dt on d.device_type = dt.id - where d.binding_user_id = #{criteria.bindingUserId} + inner join app_device_bind_record c on d.id = c.device_id + where c.binding_user_id = #{criteria.bindingUserId} and d.device_type = #{criteria.deviceType} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttConfiguration.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttConfiguration.java index 002b6b5e..b05cb3ef 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttConfiguration.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttConfiguration.java @@ -7,12 +7,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; import org.springframework.integration.mqtt.core.MqttPahoClientFactory; -/** - * @Author: HarryLin - * @Date: 2025/3/20 14:40 - * @Company: 北京红山信息科技研究院有限公司 - * @Email: linyun@***.com.cn - **/ + @Configuration public class MqttConfiguration { @Autowired diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttInboundConfiguration.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttInboundConfiguration.java index 14a83eca..de67566b 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttInboundConfiguration.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttInboundConfiguration.java @@ -15,12 +15,7 @@ import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; -/** - * @Author: HarryLin - * @Date: 2025/3/20 14:54 - * @Company: 北京红山信息科技研究院有限公司 - * @Email: linyun@***.com.cn - **/ + @Configuration public class MqttInboundConfiguration { @Autowired diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttOutboundConfiguration.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttOutboundConfiguration.java index 8ae31fc8..cb1bcb00 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttOutboundConfiguration.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttOutboundConfiguration.java @@ -12,12 +12,6 @@ import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; -/** - * @Author: HarryLin - * @Date: 2025/3/20 15:46 - * @Company: 北京红山信息科技研究院有限公司 - * @Email: linyun@***.com.cn - **/ @Configuration @Slf4j public class MqttOutboundConfiguration { diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttPropertiesConfig.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttPropertiesConfig.java index 9ae33740..9745925d 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttPropertiesConfig.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttPropertiesConfig.java @@ -4,12 +4,7 @@ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; -/** - * @Author: HarryLin - * @Date: 2025/3/20 14:32 - * @Company: 北京红山信息科技研究院有限公司 - * @Email: linyun@***.com.cn - **/ + @Data @ConfigurationProperties(prefix = "mqtt") @Component diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/receiver/ReceiverMessageHandler.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/receiver/ReceiverMessageHandler.java index c9df7cef..549f0697 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/receiver/ReceiverMessageHandler.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/receiver/ReceiverMessageHandler.java @@ -1,25 +1,36 @@ package com.fuyuanshen.system.mqtt.receiver; +import cn.hutool.core.lang.Dict; +import com.fuyuanshen.common.core.utils.ImageToCArrayConverter; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.AppLoginHelper; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.system.mqtt.config.MqttGateway; +import com.fuyuanshen.system.mqtt.constants.MqttConstants; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessagingException; import org.springframework.stereotype.Service; -import java.util.Objects; +import java.util.*; + +import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal; -/** - * @Author: HarryLin - * @Date: 2025/3/20 15:24 - * @Company: 北京红山信息科技研究院有限公司 - * @Email: linyun@***.com.cn - **/ @Service @Slf4j public class ReceiverMessageHandler implements MessageHandler { + + @Autowired + private MqttGateway mqttGateway; + @Override public void handleMessage(Message message) throws MessagingException{ + String tenantId = AppLoginHelper.getTenantId() != null ? AppLoginHelper.getTenantId() : LoginHelper.getTenantId(); Object payload = message.getPayload(); MessageHeaders headers = message.getHeaders(); String receivedTopic = Objects.requireNonNull(headers.get("mqtt_receivedTopic")).toString(); @@ -27,6 +38,53 @@ public class ReceiverMessageHandler implements MessageHandler { String timestamp = Objects.requireNonNull(headers.get("timestamp")).toString(); log.info("MQTT payload= {} \n receivedTopic = {} \n receivedQos = {} \n timestamp = {}" ,payload,receivedTopic,receivedQos,timestamp); + Dict payloadDict = JsonUtils.parseMap(payload.toString()); + if(receivedTopic ==null){ + return; + } + if(payloadDict == null){ + return; + } + String state = payloadDict.getStr("state"); + byte[] convertArr = ImageToCArrayConverter.convertStringToByteArray( state); + if(convertArr.length>0){ + byte val1 = convertArr[0]; + byte val2 = convertArr[1]; + String[] subStr = receivedTopic.split("/"); + System.out.println("收到设备id: " + subStr[1]); + String deviceImei = subStr[1]; + if(val1==3){ + + if(val2==100){ + return; + } + + String data = RedisUtils.getCacheObject("894078:app_logo_data:"+deviceImei); + if(StringUtils.isEmpty(data)){ + return; + } + byte[] arr = ImageToCArrayConverter.convertStringToByteArray(data); + byte[] specificChunk = ImageToCArrayConverter.getChunk(arr, (val2-1), 512); + System.out.println("第"+val2+"块数据大小: " + specificChunk.length + " 字节"); + System.out.println("第"+val2+"块数据: " + Arrays.toString(specificChunk)); + + ArrayList intData = new ArrayList<>(); + intData.add(3); + intData.add((int) val2); + ImageToCArrayConverter.buildArr(convertHexToDecimal(specificChunk),intData); + intData.add(0); + intData.add(0); + intData.add(0); + intData.add(0); + Map map = new HashMap<>(); + map.put("instruct", intData); + mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY+deviceImei, 1 , JsonUtils.toJsonString( map)); + log.info("发送点阵数据到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+deviceImei,JsonUtils.toJsonString(map)); + } + System.out.println("val1:"+ val1); + System.out.println("val2:"+ val2); + + } } } \ No newline at end of file