From e84b9ad0dc3f9dfc64f032ede34a283e079b9dba Mon Sep 17 00:00:00 2001 From: tiger_zhou <123456> Date: Wed, 12 Oct 2022 14:08:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BA=94=E6=80=A5=E9=A2=84=E8=AD=A6=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F-=E8=AF=AD=E9=9F=B3=E6=8C=87=E4=BB=A4=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 + .../BusinessExceptionAssertEnum.java | 2 +- .../rtss/services/voice/IVoiceService.java | 2 + .../voice/baidu/BaiduVoiceServiceImpl.java | 13 ++ .../voice/huawei/HuaweiVoiceServiceImpl.java | 8 +- .../voice/xunfei/XunFeiVoiceService.java | 9 ++ .../cbtc/ATS/operation/Operation.java | 6 +- .../cbtc/command/VoiceCommandHandler.java | 26 ++++ .../command/service/VoiceCommandService.java | 115 ++++++++++++++++++ .../conversation/SimulationVoiceHandler.java | 32 +++-- .../device/virtual/VRDeviceLogicLoop.java | 12 +- .../simulation/cbtc/member/MemberManager.java | 1 + 12 files changed, 213 insertions(+), 18 deletions(-) create mode 100644 src/main/java/club/joylink/rtss/simulation/cbtc/command/VoiceCommandHandler.java create mode 100644 src/main/java/club/joylink/rtss/simulation/cbtc/command/service/VoiceCommandService.java diff --git a/pom.xml b/pom.xml index 5d42be2c3..79d6ee2af 100644 --- a/pom.xml +++ b/pom.xml @@ -134,6 +134,11 @@ guava 31.1-jre + + com.belerweb + pinyin4j + 2.5.0 + diff --git a/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java b/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java index cdd11fe61..f7d9ddb69 100644 --- a/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java +++ b/src/main/java/club/joylink/rtss/exception/BusinessExceptionAssertEnum.java @@ -58,7 +58,7 @@ public enum BusinessExceptionAssertEnum implements BusinessExceptionAssert { INVALID_CLIENT(40031, "invalid client"), INCORRECT_VERIFICATION_CODE(40051, "incorrect verification code"), THIRD_SERVICE_CALL_EXCEPTION(40071, "the third service call exception"), - + VOICE_COMMAND_PARSE_ERROR(40061,"voice command parse error"), //支付异常 PAY_ERROR(50000, "pay error"), WECHAT_NOTIFY_ERROR(401, "wechat notify error") diff --git a/src/main/java/club/joylink/rtss/services/voice/IVoiceService.java b/src/main/java/club/joylink/rtss/services/voice/IVoiceService.java index b22d5d6ce..b5b480c2a 100644 --- a/src/main/java/club/joylink/rtss/services/voice/IVoiceService.java +++ b/src/main/java/club/joylink/rtss/services/voice/IVoiceService.java @@ -12,6 +12,8 @@ public interface IVoiceService { VoiceRecognitionResult voiceRecognition(byte[] bytes, String filePath); + VoiceRecognitionResult voiceRecognition(String fileBase64); + /** * 语音合成,返回文件路径 * diff --git a/src/main/java/club/joylink/rtss/services/voice/baidu/BaiduVoiceServiceImpl.java b/src/main/java/club/joylink/rtss/services/voice/baidu/BaiduVoiceServiceImpl.java index cf2d744e7..8451ecf35 100644 --- a/src/main/java/club/joylink/rtss/services/voice/baidu/BaiduVoiceServiceImpl.java +++ b/src/main/java/club/joylink/rtss/services/voice/baidu/BaiduVoiceServiceImpl.java @@ -12,6 +12,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Base64; /** * 百度语音识别合成服务 @@ -115,6 +116,18 @@ public class BaiduVoiceServiceImpl implements IVoiceService { } } + @Override + public VoiceRecognitionResult voiceRecognition(String fileBase64) { + try { + String base64 = fileBase64.substring(fileBase64.indexOf("base64,") + "base64,".length()); + byte[] bytes = Base64.getDecoder().decode(base64.trim()); + String filePath = VoiceFileUtils.saveFile(bytes); + return voiceRecognition(bytes, filePath); + } catch (Exception e) { + throw BusinessExceptionAssertEnum.THIRD_SERVICE_CALL_EXCEPTION.exception(e); + } + } + @Override public String synthesis(String message, String per) { try { diff --git a/src/main/java/club/joylink/rtss/services/voice/huawei/HuaweiVoiceServiceImpl.java b/src/main/java/club/joylink/rtss/services/voice/huawei/HuaweiVoiceServiceImpl.java index 3222f7691..d59414b65 100644 --- a/src/main/java/club/joylink/rtss/services/voice/huawei/HuaweiVoiceServiceImpl.java +++ b/src/main/java/club/joylink/rtss/services/voice/huawei/HuaweiVoiceServiceImpl.java @@ -64,5 +64,11 @@ public class HuaweiVoiceServiceImpl implements IVoiceService { throw BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.exception("语音识别失败", e); } } - + @Override + public VoiceRecognitionResult voiceRecognition(String fileBase64) { + String base64 = fileBase64.substring(fileBase64.indexOf("base64,") + "base64,".length()); + byte[] bytes = Base64.getDecoder().decode(base64.trim()); + String filePath = VoiceFileUtils.saveFile(bytes); + return voiceRecognition(bytes, filePath); + } } diff --git a/src/main/java/club/joylink/rtss/services/voice/xunfei/XunFeiVoiceService.java b/src/main/java/club/joylink/rtss/services/voice/xunfei/XunFeiVoiceService.java index 94663c78c..6dc580e4a 100644 --- a/src/main/java/club/joylink/rtss/services/voice/xunfei/XunFeiVoiceService.java +++ b/src/main/java/club/joylink/rtss/services/voice/xunfei/XunFeiVoiceService.java @@ -10,6 +10,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.Base64; @Service("XunFeiVoiceService") public class XunFeiVoiceService implements IVoiceService { @@ -57,6 +58,14 @@ public class XunFeiVoiceService implements IVoiceService { throw BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.exception("语音识别失败"); } + @Override + public VoiceRecognitionResult voiceRecognition(String fileBase64) { + String base64 = fileBase64.substring(fileBase64.indexOf("base64,") + "base64,".length()); + byte[] bytes = Base64.getDecoder().decode(base64.trim()); + String filePath = VoiceFileUtils.saveFile(bytes); + return voiceRecognition(bytes, filePath); + } + @Override public String synthesis(String message, String per) { return null; diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/operation/Operation.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/operation/Operation.java index adae598bd..fbf5ea817 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/operation/Operation.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/operation/Operation.java @@ -1342,7 +1342,11 @@ public class Operation { */ REGULAR_TRAIN_LINE_STATION_UPDATE(new Label[]{Label.CLIENT}), REGULAR_TRAIN_LINE_STATION_UPDATE_LIST(new Label[]{Label.CLIENT}), - REGULAR_TRAIN_LINE_STATION_UPDATE_LOAD(new Label[]{Label.CLIENT}); + REGULAR_TRAIN_LINE_STATION_UPDATE_LOAD(new Label[]{Label.CLIENT}), + /** + * 紧急应急语音命令 + */ + YJDDZH_VOICE_COMMAND(new Label[]{Label.CLIENT}); final Label[] labels; Type(Label[] labels) { diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/command/VoiceCommandHandler.java b/src/main/java/club/joylink/rtss/simulation/cbtc/command/VoiceCommandHandler.java new file mode 100644 index 000000000..109aba86b --- /dev/null +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/command/VoiceCommandHandler.java @@ -0,0 +1,26 @@ +package club.joylink.rtss.simulation.cbtc.command; + +import club.joylink.rtss.simulation.cbtc.ATS.operation.Operation; +import club.joylink.rtss.simulation.cbtc.ATS.operation.annotation.OperateHandler; +import club.joylink.rtss.simulation.cbtc.ATS.operation.annotation.OperateHandlerMapping; +import club.joylink.rtss.simulation.cbtc.Simulation; +import club.joylink.rtss.simulation.cbtc.command.service.VoiceCommandService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; + +import javax.annotation.Resource; + +@OperateHandler +@Slf4j +public class VoiceCommandHandler { + + @Resource + @Qualifier("voiceCommand") + private VoiceCommandService service; + + @OperateHandlerMapping(type = Operation.Type.YJDDZH_VOICE_COMMAND) + public void yjddzhVoiceCommand(Simulation simulation, String base64Str) { + this.service.voiceCommand(simulation,base64Str); + } + +} diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/command/service/VoiceCommandService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/command/service/VoiceCommandService.java new file mode 100644 index 000000000..e85de446f --- /dev/null +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/command/service/VoiceCommandService.java @@ -0,0 +1,115 @@ +package club.joylink.rtss.simulation.cbtc.command.service; + +import club.joylink.rtss.exception.BusinessExceptionAssertEnum; +import club.joylink.rtss.services.voice.IVoiceService; +import club.joylink.rtss.simulation.cbtc.CI.device.CiSwitchControlService; +import club.joylink.rtss.simulation.cbtc.Simulation; +import club.joylink.rtss.simulation.cbtc.conversation.SimulationVoiceHandler; +import club.joylink.rtss.simulation.cbtc.data.SimulationDataRepository; +import club.joylink.rtss.simulation.cbtc.data.map.MapElement; +import club.joylink.rtss.simulation.cbtc.data.map.Switch; +import club.joylink.rtss.vo.client.VoiceRecognitionResult; +import net.sourceforge.pinyin4j.PinyinHelper; +import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; +import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; +import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; +import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.util.Locale; +import java.util.Objects; + +@Service(value="voiceCommand") +public class VoiceCommandService { + + @Autowired + @Qualifier("baiDuVoiceService") + private IVoiceService iVoiceService; + + @Autowired + private SimulationVoiceHandler voiceHandler; + + @Autowired + private CiSwitchControlService ciSwitchControlService; + + public void voiceCommand(Simulation simulation,String base64Str){ + VoiceRecognitionResult result = this.iVoiceService.voiceRecognition(base64Str); + if(Objects.equals(false, StringUtils.hasText(result.getResult()))){ + //TODO 未获取到语音识别的内容 + return; + } + this.parseCommand(simulation,result.getResult()); + } + + + private final static String SWITCH_PINYIN = "DAOCHA"; + private final static String SWITCH_STATE_DINGWEI_PINGYIN = "DING"; + private final static String SWITCH_STATE_FANWEI_PINGYIN = "FAN"; + private void parseCommand(Simulation simulation,String result){ + String changeResultStr = voiceHandler.numHandle(result); + String pinyinContent = this.toPinYin(changeResultStr); + int switchIndex = pinyinContent.indexOf(SWITCH_PINYIN); + BusinessExceptionAssertEnum.VOICE_COMMAND_PARSE_ERROR.assertTrue(switchIndex >=0,String.format("不能解析道岔的语音指令,语音识别:%s",changeResultStr)); + String pinyinCommand = pinyinContent.substring(switchIndex + SWITCH_PINYIN.length()); + String deviceName = pinyinContent.substring(0,switchIndex); + boolean hasDingCommand = pinyinCommand.contains(SWITCH_STATE_DINGWEI_PINGYIN); + boolean hasFanCommand = pinyinCommand.contains(SWITCH_STATE_FANWEI_PINGYIN); + BusinessExceptionAssertEnum.VOICE_COMMAND_PARSE_ERROR.assertTrue(Objects.nonNull(deviceName),String.format("不能解析道岔的名称,语音识别:%s",changeResultStr)); + BusinessExceptionAssertEnum.VOICE_COMMAND_PARSE_ERROR.assertTrue(hasDingCommand || hasFanCommand,String.format("道岔的语音指令不明确,语音识别:%s",changeResultStr)); + BusinessExceptionAssertEnum.VOICE_COMMAND_PARSE_ERROR.assertNotTrue(hasDingCommand && hasFanCommand,String.format("道岔的语音指令只能有一个,语音识别:%s",changeResultStr)); + + this.turnSwitch(simulation,deviceName,hasDingCommand,hasFanCommand); + } + + private void turnSwitch(Simulation simulation,String name,boolean hasDingCommand,boolean hasFanCommand){ + + SimulationDataRepository repository = simulation.getRepository(); + Switch aSwitch = repository.getDeviceMap().values().stream() + .filter(d->d.getDeviceType() == MapElement.DeviceType.SWITCH && Objects.equals(((Switch)d).getName().toUpperCase(),name.toUpperCase())) + .map(d->(Switch) d).findFirst().orElse(null); + BusinessExceptionAssertEnum.VOICE_COMMAND_PARSE_ERROR.assertTrue(Objects.nonNull(aSwitch),String.format("未找到对应的道岔:%s",name)); + if(hasDingCommand){ + this.ciSwitchControlService.turn2NormalPosition(simulation,aSwitch); + }else if(hasFanCommand){ + this.ciSwitchControlService.turn2ReversePosition(simulation,aSwitch); + } + } + + private final static HanyuPinyinOutputFormat PIN_YIN_FORMAT = new HanyuPinyinOutputFormat(); + static{ + /** + * 输出大小写设置 + * + * LOWERCASE:输出小写 + * UPPERCASE:输出大写 + */ + PIN_YIN_FORMAT.setCaseType(HanyuPinyinCaseType.UPPERCASE); + /** + * 输出音标设置 + * + * WITH_TONE_MARK:直接用音标符(必须设置WITH_U_UNICODE,否则会抛出异常) + * WITH_TONE_NUMBER:1-4数字表示音标 + * WITHOUT_TONE:没有音标 + */ + PIN_YIN_FORMAT.setToneType(HanyuPinyinToneType.WITHOUT_TONE); + } + private String toPinYin(String content) { + StringBuilder sb = new StringBuilder(); + for(char c : content.toCharArray()){ + if(Character.toString(c).matches("[\\u4E00-\\u9FA5]+")) { + try { + String[] ss = PinyinHelper.toHanyuPinyinStringArray(c,PIN_YIN_FORMAT); + sb.append(ss[0]); + } catch (BadHanyuPinyinOutputFormatCombination e) { + e.printStackTrace(); + } + }else{ + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/conversation/SimulationVoiceHandler.java b/src/main/java/club/joylink/rtss/simulation/cbtc/conversation/SimulationVoiceHandler.java index 7e25ec047..eaa056ce0 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/conversation/SimulationVoiceHandler.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/conversation/SimulationVoiceHandler.java @@ -21,20 +21,26 @@ public class SimulationVoiceHandler { // static { // regexReplaceMap.put("一", "1"); // } + static List numReplacementList; static { replacementList = new ArrayList<>(); + numReplacementList = new ArrayList<>(); + //------------- 数字 -------------- -// replacementList.add(new Replacement("(零|洞|栋)", "0")); -// replacementList.add(new Replacement("(一|幺)", "1")); -// replacementList.add(new Replacement("(二|两)", "2")); -// replacementList.add(new Replacement("(三)", "3")); -// replacementList.add(new Replacement("(四)", "4")); -// replacementList.add(new Replacement("(五)", "5")); -// replacementList.add(new Replacement("(六)", "6")); -// replacementList.add(new Replacement("(七|拐)", "7")); -// replacementList.add(new Replacement("(八)", "8")); -// replacementList.add(new Replacement("(九)", "9")); + numReplacementList.add(new Replacement("(零|洞|栋)", "0")); + numReplacementList.add(new Replacement("(一|幺|e|E)", "1")); + numReplacementList.add(new Replacement("(二|两|r)", "2")); + numReplacementList.add(new Replacement("(三)", "3")); + numReplacementList.add(new Replacement("(四)", "4")); + numReplacementList.add(new Replacement("(五)", "5")); + numReplacementList.add(new Replacement("(六)", "6")); + numReplacementList.add(new Replacement("(七|拐)", "7")); + numReplacementList.add(new Replacement("(八)", "8")); + numReplacementList.add(new Replacement("(九)", "9")); + numReplacementList.add(new Replacement("(,|。)", "")); + numReplacementList.add(new Replacement("( )", "")); + numReplacementList.add(new Replacement("(低|地|第|敌|底|递)", "d")); //------------- 专业词汇 -------------- replacementList.add(new Replacement("(行吊|情调|神雕|性交|心跳|香蕉|星标|平调|新调)", "行调")); replacementList.add(new Replacement("(茼蒿)", "通号")); @@ -157,6 +163,12 @@ public class SimulationVoiceHandler { } } + public String numHandle(String content){ + for (Replacement replacement : numReplacementList) { + content = replacement.matchAndReplace(content); + } + return regexReplace(content); + } /** * 对语音识别内容进行仿真用词处理 * diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/device/virtual/VRDeviceLogicLoop.java b/src/main/java/club/joylink/rtss/simulation/cbtc/device/virtual/VRDeviceLogicLoop.java index e66ee544d..30900e08d 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/device/virtual/VRDeviceLogicLoop.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/device/virtual/VRDeviceLogicLoop.java @@ -26,10 +26,10 @@ public class VRDeviceLogicLoop { @Autowired private VirtualRealityDeviceService virtualRealityDeviceService; - public void runForTrainPosition(Simulation simulation){ - // 列车占压计轴区段检查 - this.updateTrainOccupySection(simulation); - } +// public void runForTrainPosition(Simulation simulation){ +// // 列车占压计轴区段检查 +// this.updateTrainOccupySection(simulation); +// } public void run(Simulation simulation) { // long start = System.currentTimeMillis(); // 虚拟真实道岔转动过程 @@ -90,6 +90,8 @@ public class VRDeviceLogicLoop { simulation.addJob(SimulationModule.VRD.name(), () -> this.run(simulation), SimulationConstants.VRD_LOOP_RATE); } public void addJobsForTrainPosition(Simulation simulation) { - simulation.addJob(SimulationModule.VRD.name(), () -> this.runForTrainPosition(simulation), SimulationConstants.VRD_LOOP_RATE); + // + simulation.addJob(SimulationModule.VRD.name(), () -> this.run(simulation), SimulationConstants.VRD_LOOP_RATE); +// simulation.addJob(SimulationModule.VRD.name(), () -> this.runForTrainPosition(simulation), SimulationConstants.VRD_LOOP_RATE); } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/member/MemberManager.java b/src/main/java/club/joylink/rtss/simulation/cbtc/member/MemberManager.java index cdd5ea668..a12f85974 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/member/MemberManager.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/member/MemberManager.java @@ -93,6 +93,7 @@ public class MemberManager { this.playRole(simulation, userId, supervisorList.get(0).getId()); break; } + case YJDDZH: case BIG_SCREEN: case ISCS: case CENTER: