应急预警系统-语音指令功能
This commit is contained in:
parent
0b266b2284
commit
e84b9ad0dc
5
pom.xml
5
pom.xml
@ -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>
|
||||||
|
@ -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")
|
||||||
|
@ -12,6 +12,8 @@ public interface IVoiceService {
|
|||||||
|
|
||||||
VoiceRecognitionResult voiceRecognition(byte[] bytes, String filePath);
|
VoiceRecognitionResult voiceRecognition(byte[] bytes, String filePath);
|
||||||
|
|
||||||
|
VoiceRecognitionResult voiceRecognition(String fileBase64);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 语音合成,返回文件路径
|
* 语音合成,返回文件路径
|
||||||
*
|
*
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 对语音识别内容进行仿真用词处理
|
* 对语音识别内容进行仿真用词处理
|
||||||
*
|
*
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user