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: