forked from dyf/fys-Multi-tenant
发送设备信息代码实现
This commit is contained in:
@ -111,6 +111,14 @@ public class AppDeviceController extends BaseController {
|
||||
return toAjax(appDeviceService.sendMessage(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送报警信息
|
||||
*/
|
||||
@PostMapping(value = "/sendAlarmMessage")
|
||||
public R<Void> sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) {
|
||||
return toAjax(appDeviceService.sendAlarmMessage(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备logo图片
|
||||
*/
|
||||
|
@ -20,10 +20,7 @@ import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
|
||||
import com.fuyuanshen.app.mapper.equipment.APPDeviceMapper;
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
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.core.utils.StringUtils;
|
||||
import com.fuyuanshen.common.core.utils.*;
|
||||
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
|
||||
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
@ -44,9 +41,12 @@ import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
|
||||
@ -89,25 +89,49 @@ public class AppDeviceBizService {
|
||||
throw new ServiceException("请选择设备");
|
||||
}
|
||||
for (Long deviceId : deviceIds) {
|
||||
Device deviceObj = deviceMapper.selectById(deviceId);
|
||||
if (deviceObj == null) {
|
||||
Device device = deviceMapper.selectById(deviceId);
|
||||
if (device == null) {
|
||||
throw new ServiceException("设备不存在" + deviceId);
|
||||
}
|
||||
|
||||
byte[] msg = ReliableTextToBitmap.textToBitmapBytes(bo.getSendMsg());
|
||||
try {
|
||||
ClassPathResource resource = new ClassPathResource("image/background.png");
|
||||
InputStream inputStream = resource.getInputStream();
|
||||
|
||||
// String backgroundImagePath = "D:\\background.png"; // 替换为实际背景图片路径
|
||||
byte[] largeData = ImageWithTextGenerate.generate160x80ImageWithText2(bo.getSendMsg(), inputStream, 25600);
|
||||
int[] ints = convertHexToDecimal(largeData);
|
||||
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+"app_send_message_data:" + device.getDeviceImei(), Arrays.toString(ints), Duration.ofSeconds(30 * 60L));
|
||||
|
||||
String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+"app_send_message_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<Integer> intData = new ArrayList<>();
|
||||
intData.add(2);
|
||||
buildArr(convertHexToDecimal(msg), intData);
|
||||
intData.add(6);
|
||||
intData.add(1);
|
||||
ImageToCArrayConverter.buildArr(convertHexToDecimal(specificChunk),intData);
|
||||
intData.add(0);
|
||||
intData.add(0);
|
||||
intData.add(0);
|
||||
intData.add(0);
|
||||
Map<String, Object> 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());
|
||||
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));
|
||||
|
||||
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("id", deviceId)
|
||||
.eq("binding_user_id", AppLoginHelper.getUserId())
|
||||
.set("send_msg", bo.getSendMsg());
|
||||
deviceMapper.update(updateWrapper);
|
||||
} catch (Exception e) {
|
||||
log.info("设备发送信息失败:{}" ,deviceId);
|
||||
}
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -390,6 +414,16 @@ public class AppDeviceBizService {
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
byte[] largeData = ImageToCArrayConverter.convertImageToCArray("E:\\workspace\\demo.png", 160, 80, 25600);
|
||||
System.out.println("长度:" + largeData.length);
|
||||
|
||||
System.out.println("原始数据大小: " + largeData.length + " 字节");
|
||||
|
||||
int[] ints = convertHexToDecimal(largeData);
|
||||
System.out.println("转换后的数据: " + Arrays.toString(ints));
|
||||
}
|
||||
|
||||
public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) {
|
||||
try {
|
||||
Device device = deviceMapper.selectById(bo.getDeviceId());
|
||||
@ -524,4 +558,45 @@ public class AppDeviceBizService {
|
||||
}
|
||||
return RedisUtils.getCacheObject("device:location:" + devices.get(0).getDeviceImei());
|
||||
}
|
||||
|
||||
public int sendAlarmMessage(AppDeviceSendMsgBo bo) {
|
||||
try {
|
||||
List<Long> deviceIds = bo.getDeviceIds();
|
||||
if (deviceIds == null || deviceIds.isEmpty()) {
|
||||
throw new ServiceException("请选择设备");
|
||||
}
|
||||
for (Long deviceId : deviceIds) {
|
||||
Device device = deviceMapper.selectById(deviceId);
|
||||
if (device == null) {
|
||||
throw new ServiceException("设备不存在" + deviceId);
|
||||
}
|
||||
|
||||
try {
|
||||
ArrayList<Integer> intData = new ArrayList<>();
|
||||
intData.add(7);
|
||||
intData.add(Integer.parseInt(bo.getInstructValue()));
|
||||
intData.add(0);
|
||||
intData.add(0);
|
||||
intData.add(0);
|
||||
intData.add(0);
|
||||
Map<String, Object> 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));
|
||||
|
||||
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("id", deviceId)
|
||||
.eq("binding_user_id", AppLoginHelper.getUserId())
|
||||
.set("send_msg", bo.getSendMsg());
|
||||
deviceMapper.update(updateWrapper);
|
||||
} catch (Exception e) {
|
||||
log.info("设备发送信息失败:{}" ,deviceId);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,11 @@ public class LightingCommandTypeConstants {
|
||||
*/
|
||||
public static final String MAIN_LIGHT_BRIGHTNESS = "Light_5";
|
||||
|
||||
/**
|
||||
* 设备发送消息
|
||||
*/
|
||||
public static final String SEND_MESSAGE = "Light_6";
|
||||
|
||||
/**
|
||||
* 定位数据 (Location Data)
|
||||
*/
|
||||
|
@ -67,7 +67,7 @@ public class DeviceBootLogoRule implements MqttMessageRule {
|
||||
map.put("instruct", intData);
|
||||
|
||||
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(), 1, JsonUtils.toJsonString(map));
|
||||
log.info("发送人员信息点阵数据到设备消息=>topic:{},payload:{}",
|
||||
log.info("发送开机LOGO点阵数据到设备消息=>topic:{},payload:{}",
|
||||
MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
|
||||
JsonUtils.toJsonString(map));
|
||||
} catch (Exception e) {
|
||||
|
@ -0,0 +1,76 @@
|
||||
package com.fuyuanshen.global.mqtt.rule;
|
||||
|
||||
import com.fuyuanshen.common.core.constant.GlobalConstants;
|
||||
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.global.mqtt.base.MqttMessageRule;
|
||||
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
|
||||
import com.fuyuanshen.global.mqtt.config.MqttGateway;
|
||||
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
|
||||
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal;
|
||||
|
||||
/**
|
||||
* 人员信息命令处理
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DeviceSendMessageRule implements MqttMessageRule {
|
||||
|
||||
private final MqttGateway mqttGateway;
|
||||
|
||||
@Override
|
||||
public String getCommandType() {
|
||||
return LightingCommandTypeConstants.SEND_MESSAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(MqttRuleContext context) {
|
||||
try {
|
||||
Byte val2 = (Byte) context.getConvertArr()[1];
|
||||
if (val2 == 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
String data = RedisUtils.getCacheObject(GlobalConstants.GLOBAL_REDIS_KEY+"app_send_message_data:" + context.getDeviceImei());
|
||||
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<Integer> intData = new ArrayList<>();
|
||||
intData.add(6);
|
||||
intData.add((int) val2);
|
||||
ImageToCArrayConverter.buildArr(convertHexToDecimal(specificChunk), intData);
|
||||
intData.add(0);
|
||||
intData.add(0);
|
||||
intData.add(0);
|
||||
intData.add(0);
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("instruct", intData);
|
||||
|
||||
mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(), 1, JsonUtils.toJsonString(map));
|
||||
log.info("发送设备信息数据到设备消息=>topic:{},payload:{}",
|
||||
MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
|
||||
JsonUtils.toJsonString(map));
|
||||
} catch (Exception e) {
|
||||
log.error("处理人员信息命令时出错", e);
|
||||
}
|
||||
}
|
||||
}
|
BIN
fys-admin/src/main/resources/image/background.png
Normal file
BIN
fys-admin/src/main/resources/image/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 397 B |
@ -242,7 +242,7 @@ public class Bitmap80x12Generator {
|
||||
return byteListToArray(byteList);
|
||||
}
|
||||
|
||||
private static byte[] byteListToArray(List<Byte> byteList) {
|
||||
public static byte[] byteListToArray(List<Byte> byteList) {
|
||||
byte[] result = new byte[byteList.size()];
|
||||
for (int i = 0; i < byteList.size(); i++) {
|
||||
result[i] = byteList.get(i);
|
||||
|
@ -33,7 +33,7 @@ public class ImageToCArrayConverter {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
byte[] largeData = convertImageToCArray("E:\\workspace\\6170_强光_160_80_2.jpg", 160, 80,25600);
|
||||
byte[] largeData = convertImageToCArray("E:\\workspace\\demo.png", 160, 80,25600);
|
||||
System.out.println("长度:"+largeData.length);
|
||||
|
||||
System.out.println("原始数据大小: " + largeData.length + " 字节");
|
||||
|
@ -0,0 +1,429 @@
|
||||
package com.fuyuanshen.common.core.utils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ImageWithTextGenerate {
|
||||
|
||||
/**
|
||||
* 生成160*80画布,嵌入背景图片并居中显示文字(支持自动换行),输出RGB565格式数据
|
||||
*
|
||||
* @param text 要显示的文字
|
||||
* @param fixedLength 固定输出长度(字节数)
|
||||
* @return RGB565格式的图像数据
|
||||
*/
|
||||
public static byte[] generate160x80ImageWithText2(String text, InputStream backgroundImageInputStream, int fixedLength) throws IOException {
|
||||
// 创建160*80的图像
|
||||
BufferedImage image = new BufferedImage(160, 80, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = image.createGraphics();
|
||||
|
||||
// 绘制白色背景
|
||||
g.setColor(Color.WHITE);
|
||||
g.fillRect(0, 0, 160, 80);
|
||||
|
||||
// 如果提供了背景图片,则绘制背景
|
||||
if (backgroundImageInputStream != null ) {
|
||||
BufferedImage backgroundImage = ImageIO.read(backgroundImageInputStream);
|
||||
// 缩放并绘制背景图片以适应160*80画布
|
||||
g.drawImage(backgroundImage, 0, 0, 160, 80, null);
|
||||
}
|
||||
|
||||
// 设置文字属性
|
||||
Font font = new Font("宋体", Font.PLAIN, 12); // 可根据需要调整字体大小
|
||||
g.setFont(font);
|
||||
g.setColor(new Color(255, 255, 255, (int)(0.6 * 255)));
|
||||
|
||||
// 关闭抗锯齿以获得清晰的点阵效果
|
||||
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 lineHeight = metrics.getHeight();
|
||||
|
||||
// 文本换行处理
|
||||
ArrayList<String> lines = wrapText(text, metrics, 120); // 160为画布宽度
|
||||
|
||||
// 计算垂直居中起始位置
|
||||
int totalTextHeight = lines.size() * lineHeight;
|
||||
int startY = (80 - totalTextHeight) / 2 + metrics.getAscent();
|
||||
|
||||
// 绘制每一行文字
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
String line = lines.get(i);
|
||||
int lineWidth = metrics.stringWidth(line);
|
||||
int x = (160 - lineWidth) / 2; // 水平居中
|
||||
int y = startY + i * lineHeight;
|
||||
g.drawString(line, x, y);
|
||||
}
|
||||
|
||||
g.dispose();
|
||||
|
||||
// 转换像素数据为RGB565格式(高位在前)
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
for (int yCoord = 0; yCoord < 80; yCoord++) {
|
||||
for (int xCoord = 0; xCoord < 160; xCoord++) {
|
||||
int rgb = image.getRGB(xCoord, yCoord);
|
||||
|
||||
// 提取RGB分量
|
||||
int r = (rgb >> 16) & 0xFF;
|
||||
int g1 = (rgb >> 8) & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
|
||||
// 转换为RGB565(5位红,6位绿,5位蓝)
|
||||
int r5 = (r >> 3) & 0x1F;
|
||||
int g6 = (g1 >> 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;
|
||||
}
|
||||
/**
|
||||
* 生成160*80画布,嵌入背景图片并居中显示文字(支持自动换行),输出RGB565格式数据
|
||||
*
|
||||
* @param text 要显示的文字
|
||||
* @param backgroundImagePath 背景图片路径
|
||||
* @param fixedLength 固定输出长度(字节数)
|
||||
* @return RGB565格式的图像数据
|
||||
*/
|
||||
public static byte[] generate160x80ImageWithText(String text, String backgroundImagePath, int fixedLength) throws IOException {
|
||||
// 创建160*80的图像
|
||||
BufferedImage image = new BufferedImage(160, 80, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = image.createGraphics();
|
||||
|
||||
// 绘制白色背景
|
||||
g.setColor(Color.WHITE);
|
||||
g.fillRect(0, 0, 160, 80);
|
||||
|
||||
// 如果提供了背景图片,则绘制背景
|
||||
if (backgroundImagePath != null && !backgroundImagePath.isEmpty()) {
|
||||
File backgroundFile = new File(backgroundImagePath);
|
||||
if (backgroundFile.exists()) {
|
||||
BufferedImage backgroundImage = ImageIO.read(backgroundFile);
|
||||
// 缩放并绘制背景图片以适应160*80画布
|
||||
g.drawImage(backgroundImage, 0, 0, 160, 80, null);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置文字属性
|
||||
Font font = new Font("宋体", Font.PLAIN, 12); // 可根据需要调整字体大小
|
||||
g.setFont(font);
|
||||
g.setColor(new Color(255, 255, 255, (int)(0.6 * 255)));
|
||||
|
||||
// 关闭抗锯齿以获得清晰的点阵效果
|
||||
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 lineHeight = metrics.getHeight();
|
||||
|
||||
// 文本换行处理
|
||||
ArrayList<String> lines = wrapText(text, metrics, 120); // 160为画布宽度
|
||||
|
||||
// 计算垂直居中起始位置
|
||||
int totalTextHeight = lines.size() * lineHeight;
|
||||
int startY = (80 - totalTextHeight) / 2 + metrics.getAscent();
|
||||
|
||||
// 绘制每一行文字
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
String line = lines.get(i);
|
||||
int lineWidth = metrics.stringWidth(line);
|
||||
int x = (160 - lineWidth) / 2; // 水平居中
|
||||
int y = startY + i * lineHeight;
|
||||
g.drawString(line, x, y);
|
||||
}
|
||||
|
||||
g.dispose();
|
||||
|
||||
// 转换像素数据为RGB565格式(高位在前)
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
for (int yCoord = 0; yCoord < 80; yCoord++) {
|
||||
for (int xCoord = 0; xCoord < 160; xCoord++) {
|
||||
int rgb = image.getRGB(xCoord, yCoord);
|
||||
|
||||
// 提取RGB分量
|
||||
int r = (rgb >> 16) & 0xFF;
|
||||
int g1 = (rgb >> 8) & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
|
||||
// 转换为RGB565(5位红,6位绿,5位蓝)
|
||||
int r5 = (r >> 3) & 0x1F;
|
||||
int g6 = (g1 >> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本换行处理
|
||||
*
|
||||
* @param text 原始文本
|
||||
* @param metrics 字体度量信息
|
||||
* @param maxWidth 最大宽度
|
||||
* @return 换行后的文本行列表
|
||||
*/
|
||||
private static ArrayList<String> wrapText(String text, FontMetrics metrics, int maxWidth) {
|
||||
ArrayList<String> lines = new ArrayList<>();
|
||||
String[] paragraphs = text.split("\n");
|
||||
|
||||
for (String paragraph : paragraphs) {
|
||||
String[] words = paragraph.split("(?<=\\S)(?=\\s)|(?<=\\s)(?=\\S)");
|
||||
StringBuilder line = new StringBuilder();
|
||||
|
||||
for (String word : words) {
|
||||
String testLine = line.toString() + word;
|
||||
int lineWidth = metrics.stringWidth(testLine);
|
||||
|
||||
if (lineWidth <= maxWidth) {
|
||||
line.append(word);
|
||||
} else {
|
||||
if (line.length() > 0) {
|
||||
lines.add(line.toString());
|
||||
line = new StringBuilder(word);
|
||||
} else {
|
||||
// 单个词就超过宽度,需要进一步拆分
|
||||
lines.addAll(wrapWord(word, metrics, maxWidth));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (line.length() > 0) {
|
||||
lines.add(line.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// 限制最大行数以适应80像素高度
|
||||
if (lines.size() > 6) { // 假设每行最多13像素高,80/13约等于6
|
||||
return (ArrayList<String>) lines.subList(0, 6);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对单个超长词进行拆分
|
||||
*
|
||||
* @param word 单词
|
||||
* @param metrics 字体度量信息
|
||||
* @param maxWidth 最大宽度
|
||||
* @return 拆分后的词列表
|
||||
*/
|
||||
private static ArrayList<String> wrapWord(String word, FontMetrics metrics, int maxWidth) {
|
||||
ArrayList<String> result = new ArrayList<>();
|
||||
StringBuilder current = new StringBuilder();
|
||||
|
||||
for (char c : word.toCharArray()) {
|
||||
String testStr = current.toString() + c;
|
||||
if (metrics.stringWidth(testStr) <= maxWidth) {
|
||||
current.append(c);
|
||||
} else {
|
||||
if (current.length() > 0) {
|
||||
result.add(current.toString());
|
||||
}
|
||||
current = new StringBuilder(String.valueOf(c));
|
||||
}
|
||||
}
|
||||
|
||||
if (current.length() > 0) {
|
||||
result.add(current.toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成160*80画布,嵌入背景图片并居中显示文字,输出RGB565格式数据(支持InputStream)
|
||||
*
|
||||
* @param text 要显示的文字
|
||||
* @param backgroundImageInputStream 背景图片输入流
|
||||
* @param fixedLength 固定输出长度(字节数)
|
||||
* @return RGB565格式的图像数据
|
||||
*/
|
||||
public static byte[] generate160x80ImageWithText(String text, InputStream backgroundImageInputStream, int fixedLength) throws IOException {
|
||||
// 创建160*80的图像
|
||||
BufferedImage image = new BufferedImage(160, 80, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = image.createGraphics();
|
||||
|
||||
// 绘制白色背景
|
||||
g.setColor(Color.WHITE);
|
||||
g.fillRect(0, 0, 160, 80);
|
||||
|
||||
// 如果提供了背景图片,则绘制背景
|
||||
if (backgroundImageInputStream != null) {
|
||||
BufferedImage backgroundImage = ImageIO.read(backgroundImageInputStream);
|
||||
// 缩放并绘制背景图片以适应160*80画布
|
||||
g.drawImage(backgroundImage, 0, 0, 160, 80, null);
|
||||
}
|
||||
|
||||
// 设置文字属性
|
||||
Font font = new Font("宋体", Font.PLAIN, 16); // 可根据需要调整字体大小
|
||||
g.setFont(font);
|
||||
g.setColor(Color.BLACK);
|
||||
|
||||
// 关闭抗锯齿以获得清晰的点阵效果
|
||||
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 textHeight = metrics.getHeight();
|
||||
|
||||
// 计算居中位置
|
||||
int x = (160 - textWidth) / 2; // 水平居中
|
||||
int y = (80 - textHeight) / 2 + metrics.getAscent(); // 垂直居中
|
||||
|
||||
// 绘制文字
|
||||
g.drawString(text, x, y);
|
||||
|
||||
g.dispose();
|
||||
|
||||
// 转换像素数据为RGB565格式(高位在前)
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
for (int yCoord = 0; yCoord < 80; yCoord++) {
|
||||
for (int xCoord = 0; xCoord < 160; xCoord++) {
|
||||
int rgb = image.getRGB(xCoord, yCoord);
|
||||
|
||||
// 提取RGB分量
|
||||
int r = (rgb >> 16) & 0xFF;
|
||||
int g1 = (rgb >> 8) & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
|
||||
// 转换为RGB565(5位红,6位绿,5位蓝)
|
||||
int r5 = (r >> 3) & 0x1F;
|
||||
int g6 = (g1 >> 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;
|
||||
}
|
||||
/**
|
||||
* 将RGB565格式的字节数组转换为BufferedImage
|
||||
*
|
||||
* @param data RGB565格式的数据
|
||||
* @param height 图像高度
|
||||
* @param width 图像宽度
|
||||
* @return 转换后的BufferedImage
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
// 创建图像
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
// 处理RGB565数据
|
||||
int dataIndex = 0;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
// 每个像素占2个字节
|
||||
if (dataIndex + 1 >= data.length) {
|
||||
return image;
|
||||
}
|
||||
|
||||
// 读取两个字节组成RGB565值
|
||||
int highByte = data[dataIndex++] & 0xFF;
|
||||
int lowByte = data[dataIndex++] & 0xFF;
|
||||
int rgb565 = (highByte << 8) | lowByte;
|
||||
|
||||
// 将RGB565转换为RGB888
|
||||
int r = ((rgb565 >> 11) & 0x1F) << 3;
|
||||
int g = ((rgb565 >> 5) & 0x3F) << 2;
|
||||
int b = (rgb565 & 0x1F) << 3;
|
||||
|
||||
int rgb = (r << 16) | (g << 8) | b;
|
||||
image.setRGB(x, y, rgb);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
// ... 原有代码 ...
|
||||
|
||||
// 测试生成160*80画布,嵌入背景图片并居中显示文字
|
||||
// String text = "现在危险,停止救援紧急撤离至安全区域";
|
||||
String text = "现在危险,停止救援,紧急撤离至安全区域!";
|
||||
String backgroundImagePath = "D:\\background.png"; // 替换为实际背景图片路径
|
||||
byte[] imageData = generate160x80ImageWithText(text, backgroundImagePath, 25600);
|
||||
|
||||
System.out.println("生成的160*80 RGB565图像数据:");
|
||||
System.out.println("数据长度: " + imageData.length + " 字节");
|
||||
// 生成预览图片
|
||||
// 生成预览图片
|
||||
BufferedImage image160x80 = convertByteArrayToImage(imageData, 80, 160);
|
||||
ImageIO.write(image160x80, "PNG", new File("D:\\bitmap_160x80_preview.png"));
|
||||
// System.out.println("成功生成160*80预览图片: D:\\bitmap_160x80_preview.png");
|
||||
// 转换为十进制数组
|
||||
// int[] decimalArray = convertHexToDecimal(imageData);
|
||||
// System.out.println("生成的十进制数据(前50个):");
|
||||
// System.out.println(Arrays.toString(Arrays.copyOf(decimalArray, Math.min(50, decimalArray.length))));
|
||||
//
|
||||
// // 将数据分割成512字节的块
|
||||
// List<byte[]> chunks = splitByteArrayIntoChunks(imageData, 512);
|
||||
// printChunkInfo(chunks);
|
||||
//
|
||||
// // 示例:获取特定块的数据
|
||||
// byte[] specificChunk = getChunk(imageData, 0, 512); // 获取第1块(索引0)
|
||||
// System.out.println("第1块数据大小: " + specificChunk.length + " 字节");
|
||||
}
|
||||
|
||||
}
|
@ -13,4 +13,10 @@ public class AppDeviceSendMsgBo {
|
||||
private String sendMsg;
|
||||
|
||||
private List<Long> deviceIds;
|
||||
|
||||
/**
|
||||
* 下发指令
|
||||
*/
|
||||
private String instructValue;
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user