应急预警系统-语音指令功能

This commit is contained in:
tiger_zhou 2022-10-12 14:08:53 +08:00
parent 0b266b2284
commit e84b9ad0dc
12 changed files with 213 additions and 18 deletions

View File

@ -134,6 +134,11 @@
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>31.1-jre</version> <version>31.1-jre</version>
</dependency> </dependency>
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -58,7 +58,7 @@ public enum BusinessExceptionAssertEnum implements BusinessExceptionAssert {
INVALID_CLIENT(40031, "invalid client"), INVALID_CLIENT(40031, "invalid client"),
INCORRECT_VERIFICATION_CODE(40051, "incorrect verification code"), INCORRECT_VERIFICATION_CODE(40051, "incorrect verification code"),
THIRD_SERVICE_CALL_EXCEPTION(40071, "the third service call exception"), THIRD_SERVICE_CALL_EXCEPTION(40071, "the third service call exception"),
VOICE_COMMAND_PARSE_ERROR(40061,"voice command parse error"),
//支付异常 //支付异常
PAY_ERROR(50000, "pay error"), PAY_ERROR(50000, "pay error"),
WECHAT_NOTIFY_ERROR(401, "wechat notify error") WECHAT_NOTIFY_ERROR(401, "wechat notify error")

View File

@ -12,6 +12,8 @@ public interface IVoiceService {
VoiceRecognitionResult voiceRecognition(byte[] bytes, String filePath); VoiceRecognitionResult voiceRecognition(byte[] bytes, String filePath);
VoiceRecognitionResult voiceRecognition(String fileBase64);
/** /**
* 语音合成返回文件路径 * 语音合成返回文件路径
* *

View File

@ -12,6 +12,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; 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 @Override
public String synthesis(String message, String per) { public String synthesis(String message, String per) {
try { try {

View File

@ -64,5 +64,11 @@ public class HuaweiVoiceServiceImpl implements IVoiceService {
throw BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.exception("语音识别失败", e); 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);
}
} }

View File

@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.util.Base64;
@Service("XunFeiVoiceService") @Service("XunFeiVoiceService")
public class XunFeiVoiceService implements IVoiceService { public class XunFeiVoiceService implements IVoiceService {
@ -57,6 +58,14 @@ public class XunFeiVoiceService implements IVoiceService {
throw BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.exception("语音识别失败"); 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 @Override
public String synthesis(String message, String per) { public String synthesis(String message, String per) {
return null; return null;

View File

@ -1342,7 +1342,11 @@ public class Operation {
*/ */
REGULAR_TRAIN_LINE_STATION_UPDATE(new Label[]{Label.CLIENT}), REGULAR_TRAIN_LINE_STATION_UPDATE(new Label[]{Label.CLIENT}),
REGULAR_TRAIN_LINE_STATION_UPDATE_LIST(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; final Label[] labels;
Type(Label[] labels) { Type(Label[] labels) {

View File

@ -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);
}
}

View File

@ -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_NUMBER1-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();
}
}

View File

@ -21,20 +21,26 @@ public class SimulationVoiceHandler {
// static { // static {
// regexReplaceMap.put("", "1"); // regexReplaceMap.put("", "1");
// } // }
static List<Replacement> numReplacementList;
static { static {
replacementList = new ArrayList<>(); replacementList = new ArrayList<>();
numReplacementList = new ArrayList<>();
//------------- 数字 -------------- //------------- 数字 --------------
// replacementList.add(new Replacement("(零|洞|栋)", "0")); numReplacementList.add(new Replacement("(零|洞|栋)", "0"));
// replacementList.add(new Replacement("(一|幺)", "1")); numReplacementList.add(new Replacement("(一|幺|e|E)", "1"));
// replacementList.add(new Replacement("(二|两)", "2")); numReplacementList.add(new Replacement("(二|两|r)", "2"));
// replacementList.add(new Replacement("(三)", "3")); numReplacementList.add(new Replacement("(三)", "3"));
// replacementList.add(new Replacement("(四)", "4")); numReplacementList.add(new Replacement("(四)", "4"));
// replacementList.add(new Replacement("(五)", "5")); numReplacementList.add(new Replacement("(五)", "5"));
// replacementList.add(new Replacement("(六)", "6")); numReplacementList.add(new Replacement("(六)", "6"));
// replacementList.add(new Replacement("(七|拐)", "7")); numReplacementList.add(new Replacement("(七|拐)", "7"));
// replacementList.add(new Replacement("(八)", "8")); numReplacementList.add(new Replacement("(八)", "8"));
// replacementList.add(new Replacement("(九)", "9")); 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("(行吊|情调|神雕|性交|心跳|香蕉|星标|平调|新调)", "行调"));
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);
}
/** /**
* 对语音识别内容进行仿真用词处理 * 对语音识别内容进行仿真用词处理
* *

View File

@ -26,10 +26,10 @@ public class VRDeviceLogicLoop {
@Autowired @Autowired
private VirtualRealityDeviceService virtualRealityDeviceService; private VirtualRealityDeviceService virtualRealityDeviceService;
public void runForTrainPosition(Simulation simulation){ // public void runForTrainPosition(Simulation simulation){
// 列车占压计轴区段检查 // // 列车占压计轴区段检查
this.updateTrainOccupySection(simulation); // this.updateTrainOccupySection(simulation);
} // }
public void run(Simulation simulation) { public void run(Simulation simulation) {
// long start = System.currentTimeMillis(); // long start = System.currentTimeMillis();
// 虚拟真实道岔转动过程 // 虚拟真实道岔转动过程
@ -90,6 +90,8 @@ public class VRDeviceLogicLoop {
simulation.addJob(SimulationModule.VRD.name(), () -> this.run(simulation), SimulationConstants.VRD_LOOP_RATE); simulation.addJob(SimulationModule.VRD.name(), () -> this.run(simulation), SimulationConstants.VRD_LOOP_RATE);
} }
public void addJobsForTrainPosition(Simulation simulation) { 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);
} }
} }

View File

@ -93,6 +93,7 @@ public class MemberManager {
this.playRole(simulation, userId, supervisorList.get(0).getId()); this.playRole(simulation, userId, supervisorList.get(0).getId());
break; break;
} }
case YJDDZH:
case BIG_SCREEN: case BIG_SCREEN:
case ISCS: case ISCS:
case CENTER: case CENTER: