模具管理改版

This commit is contained in:
杨晓东 2025-10-27 15:55:36 +08:00
parent 1b8b77a512
commit 6e49c7e779
3 changed files with 324 additions and 53 deletions

View File

@ -11,14 +11,14 @@ import java.util.List;
import java.util.Map;
/**
* 报警灯控制服务多设备版本
* 报警灯控制服务多设备版本支持声光控制+设备语音播放
*/
@Service
public class AlarmLightService {
private static final Logger log = LoggerFactory.getLogger(MqttService.class);
private static final Logger log = LoggerFactory.getLogger(AlarmLightService.class);
// 注入客户端管理器替代原直接注入单个AlarmLightTcpClient
// 注入客户端管理器管理多台报警灯TCP连接
@Autowired
private AlarmLightTcpClientManager tcpClientManager;
@ -28,25 +28,71 @@ public class AlarmLightService {
@Autowired
private AlarmLightConfig alarmLightConfig;
// 预定义指令映射保留原逻辑支持多设备共用指令
// 1. 预定义指令映射含声光控制音量语音模式
private Map<String, String> commandMap = new HashMap<>();
// 2. 设备编码与语音序号映射1-26干燥机27-30除湿干燥机
// 后续设备编码变更时仅需修改此Map
private Map<String, Integer> equipCodeToVoiceNum = new HashMap<String, Integer>() {{
// 26台干燥机对应语音1-26号
put("GZ-011", 1);
put("GZ-026", 2);
put("CS-001", 3);
put("CS-002", 4);
put("GZ-021", 5);
put("GZ-024", 6);
put("GZ-019", 7);
put("GZ-010", 8);
put("GZ-025", 9);
put("GZ-002", 10);
put("GZ-004", 11);
put("GZ-016", 12);
put("CS-003", 13);
put("GZ-005", 14);
put("CS-004", 15);
put("GZ-006", 16);
put("GZ-014", 17);
put("GZ-001", 18);
put("GZ-012", 19);
put("GZ-015", 20);
put("GZ-020", 21);
put("GZ-022", 22);
put("GZ-018", 23);
put("GZ-013", 24);
put("GZ-017", 25);
put("GZ-008", 26);
// 4台除湿干燥机对应语音27-30号
put("GZ-007", 27);
put("GZ-009", 28);
put("GZ-003", 29);
put("GZ-023", 30);
}};
@PostConstruct
public void init() {
initCommands(); // 初始化指令
initCommands(); // 初始化指令
subscribeMqttTopic(); // 订阅MQTT控制主题
}
/**
* 初始化报警灯指令集按硬件提供的串口指令配置
*/
private void initCommands() {
commandMap.put("TURN_ON", "01 06 04 0E 00 03 A9 38"); // 打开报警灯
commandMap.put("TURN_OFF", "01 06 04 0E 00 00 E9 39"); // 关闭报警灯
commandMap.put("QUERY_STATUS", "01 03 00 00 00 01 84 0A"); // 查询状态
commandMap.put("SLOW_BLINK", "此处替换为慢闪的十六进制指令"); // 补充慢闪指令
commandMap.put("FAST_BLINK", "此处替换为快闪的十六进制指令"); // 补充快闪指令
commandMap.put("TURN_ON", "01 06 04 0E 00 03 A9 38"); // 打开声音和灯光
commandMap.put("TURN_OFF", "01 06 04 0E 00 00 E9 39"); // 关闭声光
commandMap.put("TURN_ON_SOUND", "01 06 04 0E 00 01 28 F9"); // 单独打开声音
commandMap.put("TURN_ON_LIGHT", "01 06 04 0E 00 02 68 F8"); // 单独打开灯光
commandMap.put("VOLUME_LEVEL_1", "01 06 04 0F 00 01 79 39"); // 音量1级
commandMap.put("VOLUME_LEVEL_30", "01 06 04 0F 00 1E 38 F1"); // 音量30级
commandMap.put("SINGLE_LOOP", "01 06 04 11 00 01 19 3F"); // 单曲循环模式
commandMap.put("SINGLE_PLAY", "01 06 04 11 00 02 59 3E"); // 单曲播放模式
commandMap.put("QUERY_STATUS", "01 03 00 00 00 01 84 0A"); // 查询报警灯状态
commandMap.put("SLOW_BLINK", ""); // 若有慢闪指令替换为空字符串
commandMap.put("FAST_BLINK", ""); // 若有快闪指令替换为空字符串
}
/**
* 订阅MQTT控制主题保留原逻辑
* 订阅MQTT控制主题接收外部控制指令
*/
private void subscribeMqttTopic() {
mqttService.addMessageListener(message -> {
@ -56,7 +102,7 @@ public class AlarmLightService {
}
/**
* 处理MQTT控制消息核心修改按deviceId分发指令
* 处理MQTT控制消息支持声光控制语音播放
*/
private void handleMqttControlMessage(String payload) {
try {
@ -65,41 +111,94 @@ public class AlarmLightService {
if ("alarm_light_control".equals(type)) {
String command = json.getString("command");
String deviceId = json.getString("deviceId"); // 从MQTT消息获取目标设备ID
// 1. 根据deviceId获取对应客户端
String deviceId = json.getString("deviceId");
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
// 校验客户端是否存在
if (client == null) {
sendControlResult(deviceId, command, false, "未找到对应设备");
sendControlResult(deviceId, command, false, "未找到对应报警灯设备");
return;
}
// 2. 发送指令修改为通过客户端实例发送
// 分支1处理语音播放指令需传入equipmentCode
if ("PLAY_VOICE".equals(command)) {
String equipmentCode = json.getString("equipmentCode");
if (equipmentCode == null || equipmentCode.trim().isEmpty()) {
sendControlResult(deviceId, command, false, "缺少参数equipmentCode设备编码");
return;
}
// 生成语音播放指令并发送
String voiceCommand = generateVoiceCommand(equipmentCode);
if (voiceCommand == null) {
sendControlResult(deviceId, command, false, "语音指令生成失败(设备编码无效)");
return;
}
boolean success = client.sendHexCommand(voiceCommand);
sendControlResult(deviceId, command, success,
success ? "语音指令发送成功" : "语音指令发送失败");
return;
}
// 分支2处理普通声光控制指令TURN_ON/TURN_OFF等
boolean success = sendCommand(client, command);
// 3. 发送控制结果保留原逻辑
sendControlResult(deviceId, command, success, success ? "指令发送成功" : "指令发送失败");
sendControlResult(deviceId, command, success,
success ? "指令发送成功" : "指令发送失败");
}
} catch (Exception e) {
log.error("处理MQTT控制消息失败: {}", e.getMessage());
log.error("处理MQTT控制消息异常: {}", e.getMessage(), e);
}
}
/**
* 发送指令修改为接收客户端实例而非单例
* 发送预定义指令支持普通声光控制
*/
public boolean sendCommand(AlarmLightTcpClient client, String command) {
try {
// 先从预定义指令获取没有则直接当作十六进制指令
// 优先从预定义指令获取无匹配则当作自定义十六进制指令
String hexCommand = commandMap.getOrDefault(command.toUpperCase(), command);
if (hexCommand.trim().isEmpty()) {
log.warn("设备[{}]指令为空,指令类型:{}", client.getDeviceId(), command);
return false;
}
return client.sendHexCommand(hexCommand);
} catch (Exception e) {
log.error("设备[{}]发送指令失败: {}", client.getDeviceId(), e.getMessage());
log.error("设备[{}]发送指令失败,指令类型:{}", client.getDeviceId(), command, e);
return false;
}
}
/**
* 发送控制结果到MQTT抽取为独立方法便于复用
* 生成语音播放指令修改后适配001-030的3位文件编号
* 指令格式01 06 04 10 [文件夹(01)] [3位文件编号转十六进制] [CRC16校验位]
*/
private String generateVoiceCommand(String equipmentCode) {
// 1. 获取语音序号1-30对应文件编号001-030
Integer voiceNum = equipCodeToVoiceNum.get(equipmentCode);
if (voiceNum == null || voiceNum < 1 || voiceNum > 30) {
log.error("设备编码[{}]无匹配语音序号需1-30对应文件001-030", equipmentCode);
return null;
}
// 2. 关键修改将序号格式化为3位数字字符串如1"001"30"030"
String voiceFileNum = String.format("%03d", voiceNum); // 3位文件编号
// 3. 将3位数字字符串转十六进制"001"0x0001"00 01""030"0x001E"00 1E"
int numInt = Integer.parseInt(voiceFileNum);
String numHex = String.format("%04X", numInt); // 转4位十六进制对应2字节
String numHexSplit = numHex.substring(0, 2) + " " + numHex.substring(2, 4); // 拆分为2字节加空格
// 4. 构建指令前缀文件夹固定为02
String folderHex = "01"; // 语音文件存放在第二个文件夹
String prefix = "01 06 04 10 " + folderHex + " " + numHexSplit; // 前6字节指令
// 5. 计算CRC16-Modbus校验位小端模式逻辑不变
String crcHex = Crc16Calculator.calculate(prefix.replaceAll(" ", ""));
// 6. 拼接完整指令前缀+校验位
return prefix + " " + crcHex;
}
/**
* 发送控制结果到MQTT供前端/其他服务监听
*/
private void sendControlResult(String deviceId, String command, boolean success, String msg) {
String resultTopic = "alarm_light/" + deviceId + "/control_result";
@ -110,11 +209,90 @@ public class AlarmLightService {
result.put("message", msg);
result.put("timestamp", System.currentTimeMillis());
mqttService.sendMessage(resultTopic, result.toJSONString());
log.info("设备[{}]控制结果已发布到MQTT: {}", deviceId, result);
try {
mqttService.sendMessage(resultTopic, result.toJSONString());
log.info("报警灯[{}]控制结果已发布,主题:{},内容:{}", deviceId, resultTopic, result);
} catch (Exception e) {
log.error("发布报警灯[{}]控制结果失败", deviceId, e);
}
}
// ---------------------- 以下为单设备快捷方法按需保留 ----------------------
// ---------------------- 批量控制方法报警触发核心 ----------------------
/**
* 批量打开所有报警灯声光同时开启
*/
public void turnOnAll() {
List<AlarmLightConfig.DeviceConfig> devices = alarmLightConfig.getDevices();
if (devices == null || devices.isEmpty()) {
log.warn("无配置的报警灯设备,无法执行批量打开");
return;
}
for (AlarmLightConfig.DeviceConfig device : devices) {
String deviceId = device.getDeviceId();
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
if (client == null) {
log.warn("报警灯[{}]客户端不存在,跳过批量打开", deviceId);
continue;
}
boolean success = sendCommand(client, "TURN_ON");
log.info("报警灯[{}]批量打开结果:{}", deviceId, success ? "成功" : "失败");
}
}
/**
* 批量关闭所有报警灯声光同时关闭
*/
public void turnOffAll() {
List<AlarmLightConfig.DeviceConfig> devices = alarmLightConfig.getDevices();
if (devices == null || devices.isEmpty()) {
log.warn("无配置的报警灯设备,无法执行批量关闭");
return;
}
for (AlarmLightConfig.DeviceConfig device : devices) {
String deviceId = device.getDeviceId();
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
if (client == null) {
log.warn("报警灯[{}]客户端不存在,跳过批量关闭", deviceId);
continue;
}
boolean success = sendCommand(client, "TURN_OFF");
log.info("报警灯[{}]批量关闭结果:{}", deviceId, success ? "成功" : "失败");
}
}
/**
* 批量播放指定设备的语音所有报警灯同时播放同一设备语音
*/
public void playVoiceOnAll(String equipmentCode) {
List<AlarmLightConfig.DeviceConfig> devices = alarmLightConfig.getDevices();
if (devices == null || devices.isEmpty()) {
log.warn("无配置的报警灯设备,无法执行批量语音播放");
return;
}
// 生成当前设备的语音指令所有报警灯共用同一指令
String voiceCommand = generateVoiceCommand(equipmentCode);
if (voiceCommand == null) {
log.error("设备[{}]语音指令生成失败,终止批量播放", equipmentCode);
return;
}
for (AlarmLightConfig.DeviceConfig device : devices) {
String deviceId = device.getDeviceId();
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
if (client == null) {
log.warn("报警灯[{}]客户端不存在,跳过语音播放", deviceId);
continue;
}
boolean success = client.sendHexCommand(voiceCommand);
log.info("报警灯[{}]播放设备[{}]语音结果:{},指令:{}",
deviceId, equipmentCode, success ? "成功" : "失败", voiceCommand);
}
}
// ---------------------- 单设备控制方法按需调用 ----------------------
public boolean turnOn(String deviceId) {
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
return client != null && sendCommand(client, "TURN_ON");
@ -125,14 +303,34 @@ public class AlarmLightService {
return client != null && sendCommand(client, "TURN_OFF");
}
public boolean slowBlink(String deviceId) {
public boolean turnOnSound(String deviceId) {
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
return client != null && sendCommand(client, "SLOW_BLINK");
return client != null && sendCommand(client, "TURN_ON_SOUND");
}
public boolean fastBlink(String deviceId) {
public boolean turnOnLight(String deviceId) {
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
return client != null && sendCommand(client, "FAST_BLINK");
return client != null && sendCommand(client, "TURN_ON_LIGHT");
}
public boolean setVolumeLevel1(String deviceId) {
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
return client != null && sendCommand(client, "VOLUME_LEVEL_1");
}
public boolean setVolumeLevel30(String deviceId) {
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
return client != null && sendCommand(client, "VOLUME_LEVEL_30");
}
public boolean setSingleLoop(String deviceId) {
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
return client != null && sendCommand(client, "SINGLE_LOOP");
}
public boolean setSinglePlay(String deviceId) {
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
return client != null && sendCommand(client, "SINGLE_PLAY");
}
public boolean queryStatus(String deviceId) {
@ -150,33 +348,106 @@ public class AlarmLightService {
return client != null && client.isConnected();
}
// 新增批量控制所有报警灯同时打开
public void turnOnAll() {
// 获取配置中的所有设备ID
/**
* 核心报警方法批量切换所有报警灯到目标设备语音 再打开声光确保语音对应
* @param equipmentCode 报警设备编码如GZ-011
*/
public void triggerAlarmWithVoice(String equipmentCode) {
List<AlarmLightConfig.DeviceConfig> devices = alarmLightConfig.getDevices();
if (devices == null || devices.isEmpty()) {
System.out.println("⚠️ 未配置任何报警灯设备");
log.warn("无配置的报警灯设备,无法触发报警");
return;
}
// 遍历所有deviceId逐个发送打开指令
// 1. 生成目标设备的语音切换指令第一步切语音
String voiceSwitchCommand = generateVoiceCommand(equipmentCode);
if (voiceSwitchCommand == null) {
log.error("设备[{}]语音切换指令生成失败,终止报警", equipmentCode);
return;
}
// 2. 遍历所有报警灯执行切语音开声光
for (AlarmLightConfig.DeviceConfig device : devices) {
String deviceId = device.getDeviceId();
boolean success = turnOn(deviceId); // 调用单个设备的打开方法
System.out.println("设备[" + deviceId + "] 批量打开" + (success ? "成功" : "失败"));
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
if (client == null) {
log.warn("报警灯[{}]客户端不存在,跳过报警", deviceId);
continue;
}
// 步骤1先发送语音切换指令切到目标设备语音
boolean switchSuccess = client.sendHexCommand(voiceSwitchCommand);
if (!switchSuccess) {
log.error("报警灯[{}]切换语音失败,指令:{}", deviceId, voiceSwitchCommand);
continue;
}
// 步骤2再发送打开声光指令播放已切换好的语音
boolean alarmSuccess = sendCommand(client, "TURN_ON");
log.info("报警灯[{}]处理结果:语音切换{},声光开启{},目标设备:{}",
deviceId, switchSuccess ? "成功" : "失败",
alarmSuccess ? "成功" : "失败", equipmentCode);
}
}
// 可选批量关闭所有报警灯
public void turnOffAll() {
/**
* 批量关闭所有报警灯关闭声光重置状态
*/
public void stopAllAlarm() {
List<AlarmLightConfig.DeviceConfig> devices = alarmLightConfig.getDevices();
if (devices == null || devices.isEmpty()) {
System.out.println("⚠️ 未配置任何报警灯设备");
log.warn("无配置的报警灯设备,无法关闭报警");
return;
}
for (AlarmLightConfig.DeviceConfig device : devices) {
String deviceId = device.getDeviceId();
boolean success = turnOff(deviceId);
System.out.println("设备[" + deviceId + "] 批量关闭" + (success ? "成功" : "失败"));
AlarmLightTcpClient client = tcpClientManager.getClientByDeviceId(deviceId);
if (client == null) {
log.warn("报警灯[{}]客户端不存在,跳过关闭", deviceId);
continue;
}
// 发送关闭声光指令
boolean success = sendCommand(client, "TURN_OFF");
log.info("报警灯[{}]关闭声光结果:{}", deviceId, success ? "成功" : "失败");
}
}
// ---------------------- 内部工具类CRC16-Modbus校验位计算小端模式 ----------------------
private static class Crc16Calculator {
private static final int POLYNOMIAL = 0x8005; // CRC16-Modbus多项式
private static final int INITIAL_VALUE = 0xFFFF; // 初始值
/**
* 计算十六进制字符串的CRC16校验位返回小端模式的十六进制字符串空格分隔
*/
public static String calculate(String hexString) {
try {
// 十六进制字符串转字节数组
byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0; i < bytes.length; i++) {
int pos = i * 2;
bytes[i] = (byte) Integer.parseInt(hexString.substring(pos, pos + 2), 16);
}
// 计算CRC16
int crc = INITIAL_VALUE;
for (byte b : bytes) {
crc ^= (b & 0xFF);
for (int i = 0; i < 8; i++) {
crc = (crc & 0x0001) != 0 ? (crc >> 1) ^ POLYNOMIAL : crc >> 1;
}
}
// 转换为小端模式低字节在前高字节在后
int crcLow = (crc & 0xFF); // 低8位
int crcHigh = (crc >> 8) & 0xFF; // 高8位
return String.format("%02X %02X", crcLow, crcHigh);
} catch (Exception e) {
log.error("CRC16校验位计算失败输入十六进制字符串{}", hexString, e);
return "";
}
}
}
}

View File

@ -287,8 +287,8 @@ public class AutoCollectService {
updateWrapper.eq(DryEquipmentInfo::getEquipmentCode, data.getEquipmentCode())
.set(DryEquipmentInfo::getStatus, '2');
equipmentInfoMapper.update(updateWrapper);
// MQTT向报警灯发送报警
alarmLightService.turnOnAll();
/** MQTT向报警灯发送报警 */
alarmLightService.triggerAlarmWithVoice(data.getEquipmentCode());
}
if (parseThresholdValue(threshold.getMinValue()) > parseThresholdValue(data.getDataValue())) {
// 插入报警日志
@ -305,8 +305,8 @@ public class AutoCollectService {
// 修改设备状态
equipmentInfoMapper.update(updateWrapper);
logMapper.insert(log);
// MQTT向报警灯发送报警
alarmLightService.turnOnAll();
/** MQTT向报警灯发送报警 */
alarmLightService.triggerAlarmWithVoice(data.getEquipmentCode());
} else {
return;
}

View File

@ -151,12 +151,12 @@ public class MqttConsole implements CommandLineRunner {
case "OFF":
success = alarmLightService.turnOff(deviceId);
break;
case "SLOW":
success = alarmLightService.slowBlink(deviceId);
break;
case "FAST":
success = alarmLightService.fastBlink(deviceId);
break;
// case "SLOW":
// success = alarmLightService.slowBlink(deviceId);
// break;
// case "FAST":
// success = alarmLightService.fastBlink(deviceId);
// break;
case "STATUS":
success = alarmLightService.queryStatus(deviceId);
break;