You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
906 lines
32 KiB
Go
906 lines
32 KiB
Go
3 years ago
|
package icssessionmanager
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"runtime/debug"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
"sync/atomic"
|
||
|
"time"
|
||
|
|
||
|
"gitlab.com/cinnamon/voiceagent/icsconf"
|
||
|
"gitlab.com/cinnamon/voiceagent/icserror"
|
||
|
"gitlab.com/cinnamon/voiceagent/icsevent"
|
||
|
"gitlab.com/cinnamon/voiceagent/icshttpclient"
|
||
|
"gitlab.com/cinnamon/voiceagent/icslog"
|
||
|
"gitlab.com/cinnamon/voiceagent/icsmediaconv"
|
||
|
"gitlab.com/cinnamon/voiceagent/icsnet"
|
||
|
"gitlab.com/cinnamon/voiceagent/icspacketparser"
|
||
|
"gitlab.com/cinnamon/voiceagent/icssvc"
|
||
|
"gitlab.com/cinnamon/voiceagent/icsws"
|
||
|
"gitlab.com/cinnamon/voiceagent/recorddata"
|
||
|
"gitlab.com/cinnamon/voiceagent/recorddata/readcallsignal"
|
||
|
"gitlab.com/cinnamon/voiceagent/recorddata/responsecs"
|
||
|
"gitlab.com/cinnamon/voiceagent/stt"
|
||
|
"gitlab.com/ics_cinnamon/joy4/format"
|
||
|
)
|
||
|
|
||
|
type IcsSession struct {
|
||
|
ID int
|
||
|
|
||
|
goroutineID int
|
||
|
|
||
|
//bot
|
||
|
callId string
|
||
|
srcPort int
|
||
|
dstPort int
|
||
|
direction bool
|
||
|
Station string
|
||
|
sstation string
|
||
|
botToken string
|
||
|
url string
|
||
|
custid string
|
||
|
|
||
|
eventSystem *icsevent.EventSystem
|
||
|
|
||
|
//manage session
|
||
|
lastTimestamp time.Time
|
||
|
isStart int32
|
||
|
|
||
|
//media
|
||
|
invitePayloads []string
|
||
|
payloadType icspacketparser.PayloadType
|
||
|
isFoundPayload bool
|
||
|
TxConverter *icsmediaconv.Converter
|
||
|
RxConverter *icsmediaconv.Converter
|
||
|
txSeq int //first TX seq num
|
||
|
rxSeq int //first RX seq num
|
||
|
STTTx *stt.STT
|
||
|
STTRx *stt.STT
|
||
|
cs *readcallsignal.CallSignal
|
||
|
|
||
|
isSTTStartTimer *time.Timer
|
||
|
isSTTStart bool
|
||
|
|
||
|
dtmf string
|
||
|
|
||
|
//websocket
|
||
|
WSConn *icsws.WSClient
|
||
|
|
||
|
//agent config
|
||
|
AgentConfig *icsconf.AgentConfig
|
||
|
|
||
|
//bot
|
||
|
isBotStart bool
|
||
|
botStatus *icshttpclient.ProcessResp
|
||
|
TcpConn *icsnet.IcsTCPNet
|
||
|
|
||
|
//call info
|
||
|
StartTimeStamp int64
|
||
|
ChannelID string
|
||
|
ServerID string
|
||
|
|
||
|
/*//////////////////
|
||
|
rxFile *os.File
|
||
|
txFile *os.File
|
||
|
*/ //////////////////
|
||
|
|
||
|
//Voice capture conn
|
||
|
CallSignalNeter icsnet.IcsNeter
|
||
|
VoiceNeter icsnet.IcsNeter
|
||
|
|
||
|
MethodAutomata int
|
||
|
|
||
|
m *sync.Mutex
|
||
|
}
|
||
|
|
||
|
type IcsSessionID int
|
||
|
|
||
|
const MAX_SESSION_IDLE = time.Hour
|
||
|
|
||
|
const (
|
||
|
ICS_INBOUND_CALL = true
|
||
|
ICS_OUTBOUND_CALL = false
|
||
|
)
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
//session routine
|
||
|
|
||
|
//init ffmpeg
|
||
|
func init() {
|
||
|
format.RegisterAll()
|
||
|
}
|
||
|
|
||
|
//on/off check the session idle
|
||
|
func (s *IcsSession) Start() {
|
||
|
|
||
|
s.SetTimestamp(time.Now())
|
||
|
//s.isStart = true
|
||
|
atomic.StoreInt32(&s.isStart, 1)
|
||
|
}
|
||
|
|
||
|
func (s *IcsSession) Stop() {
|
||
|
l := icslog.GetIcsLog()
|
||
|
|
||
|
defer func() {
|
||
|
if err := recover(); err != nil {
|
||
|
switch v := err.(type) {
|
||
|
case error:
|
||
|
icserror.ICSERRNETNotConnectError.SetError(v)
|
||
|
l.Printf(icslog.LOG_LEVEL_WARN, s.ID, "PANIC!\n%s", string(debug.Stack()))
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
l.Print(icslog.LOG_LEVEL_INFO, s.ID, "Session Stop")
|
||
|
l.Print(icslog.LOG_LEVEL_INFO, s.ID, "Timer Stop")
|
||
|
s.isSTTStartTimer.Stop()
|
||
|
atomic.StoreInt32(&s.isStart, 0)
|
||
|
}
|
||
|
|
||
|
func (s *IcsSession) Run() *icserror.IcsError {
|
||
|
// MAX_SESSION_IDLE := time.Second * time.Duration(icsconf.GetIcsConfig().SessionExpired.SSec)
|
||
|
mySessionID := s.GetSessionID()
|
||
|
l := icslog.GetIcsLog()
|
||
|
//conf := icsconf.GetIcsConfig()
|
||
|
|
||
|
s.goroutineID = goid()
|
||
|
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, int(mySessionID), "Session [%d] READY\n", mySessionID)
|
||
|
|
||
|
svc := icssvc.GetServiceStatus()
|
||
|
for !svc.GetExit() && !svc.GetStop() {
|
||
|
//get event
|
||
|
h := icsevent.NewEventH()
|
||
|
evt, evterr := h.GetEvent(int(mySessionID))
|
||
|
if evt == nil {
|
||
|
if evterr != nil {
|
||
|
//evterr.Print()
|
||
|
time.Sleep(time.Millisecond)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
duration := time.Since(s.lastTimestamp)
|
||
|
|
||
|
if atomic.LoadInt32(&s.isStart) == 1 && duration >= MAX_SESSION_IDLE {
|
||
|
//Session expired!
|
||
|
err := icserror.ICSERRSESSMaxSessionIdle
|
||
|
|
||
|
logmsg := fmt.Sprintf("Session expired. Duration: %v(%v) - %s\n", duration, s.lastTimestamp, err.GetMessage())
|
||
|
l.Print(icslog.LOG_LEVEL_INFO, s.ID, logmsg)
|
||
|
|
||
|
s.Stop()
|
||
|
serr := s.RemoveSession()
|
||
|
if serr != nil {
|
||
|
//serr.Print()
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "RemoveSession Error %s", serr.GetMessage())
|
||
|
}
|
||
|
|
||
|
//return err
|
||
|
}
|
||
|
|
||
|
time.Sleep(time.Millisecond)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
//refresh session timestamp
|
||
|
s.SetTimestamp(time.Now())
|
||
|
|
||
|
//l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Get Event[%d]", evt.ID)
|
||
|
|
||
|
data := evt.GetData()
|
||
|
|
||
|
switch v := (*data).(type) {
|
||
|
case *readcallsignal.CallSignal:
|
||
|
s.cs = v
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Get CallSignal Event[%d] - %+v", evt.ID, s.cs)
|
||
|
//call start
|
||
|
if s.cs.EventType == "S" { //call started
|
||
|
switch s.AgentConfig.Action {
|
||
|
case "voicecapture":
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Call Started - Station: %s, Tel: %s", s.cs.Station, s.cs.CustID)
|
||
|
//fmt.Println("voice capture")
|
||
|
s.STTRx = stt.NewSTT(nil)
|
||
|
s.STTTx = stt.NewSTT(nil)
|
||
|
|
||
|
s.isSTTStart = true
|
||
|
|
||
|
go s.RecvVoice()
|
||
|
case "voicegateway":
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Call Started - Station: %s, Tel: %s, %+v", s.cs.Station, s.cs.CustID, s.cs)
|
||
|
|
||
|
//agentname := s.AgentConfig.Name
|
||
|
|
||
|
s.STTRx = stt.NewSTT(nil)
|
||
|
go s.RecvVoice()
|
||
|
|
||
|
url := fmt.Sprintf("http://%s:%d%s",
|
||
|
s.AgentConfig.BotConfig.IP,
|
||
|
s.AgentConfig.BotConfig.Port,
|
||
|
s.AgentConfig.BotConfig.URL2)
|
||
|
//remove null terminator
|
||
|
var custid, agentid string
|
||
|
n := bytes.Index([]byte(s.cs.CustID), []byte{0})
|
||
|
if n != -1 {
|
||
|
custid = string([]byte(s.cs.CustID)[:n])
|
||
|
} else {
|
||
|
custid = s.cs.CustID
|
||
|
}
|
||
|
n = bytes.Index([]byte(s.cs.AgentID), []byte{0})
|
||
|
if n != -1 {
|
||
|
agentid = string([]byte(s.cs.AgentID)[:n])
|
||
|
} else {
|
||
|
agentid = s.cs.AgentID
|
||
|
}
|
||
|
|
||
|
n = bytes.Index([]byte(s.cs.Station), []byte{0})
|
||
|
var sstation string
|
||
|
if n != -1 {
|
||
|
sstation = string([]byte(s.cs.Station)[:n])
|
||
|
} else {
|
||
|
sstation = s.cs.Station
|
||
|
}
|
||
|
|
||
|
//set callid
|
||
|
timei := time.Now().UnixNano()
|
||
|
if len(s.callId) == 0 {
|
||
|
s.callId = fmt.Sprintf("%d@%s", timei, agentid)
|
||
|
fmt.Println("callid :", s.callId)
|
||
|
}
|
||
|
|
||
|
s.url = url
|
||
|
s.custid = custid
|
||
|
s.sstation = sstation
|
||
|
|
||
|
botRequest := icshttpclient.NewPostProcess(s.url, "INIT", "", s.custid, "ICS_RCP", s.callId, s.sstation, "", "")
|
||
|
////////////////////////////////////////////////
|
||
|
//post event to the session
|
||
|
h := icsevent.NewEventH()
|
||
|
var evt *icsevent.Event
|
||
|
var evtErr *icserror.IcsError
|
||
|
if evt, evtErr = h.AllocEvent(botRequest); evtErr != nil {
|
||
|
l.Print(icslog.LOG_LEVEL_ERROR, s.ID, "Response Error")
|
||
|
s.SendVoiceGatewayByeSignal()
|
||
|
continue
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, -1, "Post BOT Request[%d] to Session[%03d]", evt.ID, s.ID)
|
||
|
h.PostEvent(s.ID, evt)
|
||
|
|
||
|
/*
|
||
|
processResp := icshttpclient.PostProcess(url, "INIT", "", custid, "ICS_RCP", s.callId, sstation, "", "")
|
||
|
//processResp := icshttpclient.PostProcess(url, "INIT", result, custid, "ICS_RCP", s.callId, "", filename)
|
||
|
if processResp == nil {
|
||
|
l.Print(icslog.LOG_LEVEL_ERROR, s.ID, "Response Error")
|
||
|
s.SendVoiceGatewayByeSignal()
|
||
|
continue
|
||
|
} else {
|
||
|
if processResp.ResultCode == 200 {
|
||
|
s.botStatus = processResp
|
||
|
s.botToken = processResp.Token
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Response : %+v", processResp)
|
||
|
|
||
|
fmt.Println(">>> ment", processResp.AnnounceMents)
|
||
|
//tts
|
||
|
pcm, ttsErr := tts.TTS(processResp.AnnounceMents, 8000)
|
||
|
if ttsErr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "TTS Error[%s] - %s",
|
||
|
processResp.AnnounceMents, ttsErr.GetError())
|
||
|
s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
continue
|
||
|
} else {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "%s!!! TTS Result: [%s] %d bytes",
|
||
|
agentname, processResp.AnnounceMents, len(pcm))
|
||
|
|
||
|
//send tts data to voice gateway
|
||
|
r := responsecs.NewResponse(agentname, responsecs.TTS_COMMAND, responsecs.RC_SUCCESS, len(pcm))
|
||
|
addr := icsnet.NewNetAddrWithIPPort(conf.VGWCommandConfig.IP, conf.VGWCommandConfig.Port)
|
||
|
t, wlen, werr := icsnet.SendCallSignal2(&addr, nil, r.GetData())
|
||
|
//wlen, werr := s.TcpConn.Write(r.GetData())
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS header - %s [%+v]",
|
||
|
werr.GetError(), s.TcpConn)
|
||
|
s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
continue
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent TTS header : %d [%s]", wlen, r)
|
||
|
|
||
|
s.TcpConn = t
|
||
|
wlen, werr = s.TcpConn.Write(pcm)
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS data - %s",
|
||
|
werr.GetError())
|
||
|
s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
continue
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent TTS data : %d", wlen)
|
||
|
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Barge-In: %s", processResp.Data.BargeIn)
|
||
|
switch processResp.Data.BargeIn {
|
||
|
case "Y":
|
||
|
//s.StartSTT(wlen)
|
||
|
s.isSTTStart = true
|
||
|
case "N":
|
||
|
s.StartSTT(wlen)
|
||
|
//s.isSTTStart = false
|
||
|
}
|
||
|
//s.StartSTT(wlen)
|
||
|
}
|
||
|
} else {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Response Error : %+v", processResp)
|
||
|
s.SendVoiceGatewayByeSignal()
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
} else if s.cs.EventType == "D" { //dtmf
|
||
|
s.AddDtmf()
|
||
|
//s.dtmf = s.cs.InOut
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "DTMF : %s(%s|%s)", s.cs.InOut, s.dtmf, s.GetDtmf(icsconf.GetIcsConfig().DtmfEndSignal.Signal))
|
||
|
s.DTMFCB()
|
||
|
} else {
|
||
|
fmt.Println("198LINE CALL END!!")
|
||
|
}
|
||
|
case *icshttpclient.IcsHttpClient:
|
||
|
request := v
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Get http.Request Event[%d] - %+v", evt.ID, request)
|
||
|
s.DoScenario(request)
|
||
|
case *stt.STTResult:
|
||
|
sttResult := v
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Get STT Result Event[%d] - %s, %s", evt.ID, sttResult.GetResult(), sttResult.GetError().GetError())
|
||
|
s.STTResultCB(sttResult)
|
||
|
/*
|
||
|
err := sttResult.GetError()
|
||
|
if err == icserror.ICSERRSTTOK {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "STT Completed - %s", err)
|
||
|
s.STTResultCB(sttResult)
|
||
|
}
|
||
|
*/
|
||
|
case icspacketparser.SIP:
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Get SIP Event[%d]", evt.ID)
|
||
|
sip := v
|
||
|
s.analyzeSIP(&sip)
|
||
|
case icspacketparser.RTP:
|
||
|
//l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Get RTP Event[%d]", evt.ID)
|
||
|
rtp := v
|
||
|
//fmt.Println(rtp)
|
||
|
s.analyzeRTP(&rtp)
|
||
|
//default:
|
||
|
//fmt.Println(time.Now(), "NOT DEFINED EVENT TYPE!!!!!!!!!!!!!!!!!")
|
||
|
default:
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Event Type : %T", v)
|
||
|
}
|
||
|
|
||
|
evt = nil
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *IcsSession) RecvVoice() {
|
||
|
l := icslog.GetIcsLog()
|
||
|
conf := icsconf.GetIcsConfig()
|
||
|
//agentname := conf.AgentConfig[s.ID].Name
|
||
|
agentname := s.AgentConfig.Name
|
||
|
|
||
|
var uniq string
|
||
|
|
||
|
wait := new(sync.WaitGroup)
|
||
|
wait.Add(1)
|
||
|
|
||
|
l.Print(icslog.LOG_LEVEL_DEBUG2, s.ID, "Started RecvVoice")
|
||
|
|
||
|
go func() {
|
||
|
defer wait.Done()
|
||
|
start := atomic.LoadInt32(&s.isStart)
|
||
|
for start == 1 {
|
||
|
start = atomic.LoadInt32(&s.isStart)
|
||
|
//receive voice data from voice server
|
||
|
buf, rlen, rerr := s.VoiceNeter.Read(340)
|
||
|
if rerr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to Read VoiceNeter - %s-%s", rerr.GetMessage(), rerr.GetError())
|
||
|
// s.SendVoiceGatewayByeSignal()
|
||
|
break
|
||
|
}
|
||
|
|
||
|
if strings.Compare(s.AgentConfig.Action, "voicegateway") == 0 { //for barge-in
|
||
|
if !s.isSTTStart {
|
||
|
continue
|
||
|
} else {
|
||
|
//s.isSTTStartTimer.Stop()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//parsing voice data header
|
||
|
voicedata := recorddata.NewVoiceDataHDR(buf[:rlen])
|
||
|
|
||
|
switch voicedata.Type {
|
||
|
case 0: //RX
|
||
|
//var url, custid string
|
||
|
//var processResp *icshttpclient.ProcessResp
|
||
|
uniq = fmt.Sprintf("%s-RX-", s.cs.Station)
|
||
|
//file.Write(voicedata.Payload)
|
||
|
//file.Sync()
|
||
|
//STT
|
||
|
result, rmsval, err := s.STTRx.STT(voicedata)
|
||
|
// s.STTStatus = s.STTStatus{err, err}
|
||
|
|
||
|
//l.Printf(icslog.LOG_LEVEL_DEBUG2, a.ID, "[%f]%+v\n", rmsval, voicedata)
|
||
|
//fmt.Printf("[%f]%+v\n", rms, voicedata)
|
||
|
if err == icserror.ICSERRSTTContinue {
|
||
|
//fmt.Printf("RX CONT >>> %f %s\n", rmsval, result)
|
||
|
continue
|
||
|
} else if err == icserror.ICSERRSTTBargeIn {
|
||
|
//send barge-in command to voice gateway
|
||
|
r := responsecs.NewResponse(agentname, "", responsecs.BARGEIN_COMMAND, responsecs.RC_SUCCESS, 0)
|
||
|
addr := icsnet.NewNetAddrWithIPPort(conf.VGWCommandConfig.IP, conf.VGWCommandConfig.Port)
|
||
|
_, wlen, werr := icsnet.SendCallSignal2(&addr, nil, r.GetData())
|
||
|
//wlen, werr := s.TcpConn.Write(r.GetData())
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send Barge-In Command - %s [%+v]",
|
||
|
werr.GetError(), s.TcpConn)
|
||
|
continue
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent Barge-In Command : %d", wlen)
|
||
|
} else if err == icserror.ICSERRSTTOK {
|
||
|
s.isSTTStart = false
|
||
|
filename := s.STTRx.Save(uniq, conf.VoiceConfig.Path)
|
||
|
size := s.STTRx.GetVoiceBufCur()
|
||
|
s.STTRx.Close()
|
||
|
|
||
|
switch s.AgentConfig.Action {
|
||
|
case "voicegateway": //only RX!!!
|
||
|
sttResult := stt.NewSTTResult(result, err)
|
||
|
////////////////////////////////////////////////
|
||
|
//post event to the session
|
||
|
h := icsevent.NewEventH()
|
||
|
var evt *icsevent.Event
|
||
|
var evtErr *icserror.IcsError
|
||
|
if evt, evtErr = h.AllocEvent(sttResult); evtErr != nil {
|
||
|
l.Print(icslog.LOG_LEVEL_ERROR, s.ID, "Response Error")
|
||
|
s.SendVoiceGatewayByeSignal()
|
||
|
continue
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, -1, "Post STT Result[%d] to Session[%03d]", evt.ID, s.ID)
|
||
|
h.PostEvent(s.ID, evt)
|
||
|
|
||
|
/*****************************
|
||
|
if s.botStatus != nil { //processing
|
||
|
url = fmt.Sprintf("http://%s:%d%s",
|
||
|
s.AgentConfig.BotConfig.IP,
|
||
|
s.AgentConfig.BotConfig.Port,
|
||
|
s.AgentConfig.BotConfig.URL2)
|
||
|
//remove null terminator
|
||
|
n := bytes.Index([]byte(s.cs.CustID), []byte{0})
|
||
|
if n != -1 {
|
||
|
custid = string([]byte(s.cs.CustID)[:n])
|
||
|
} else {
|
||
|
custid = s.cs.CustID
|
||
|
}
|
||
|
var agentid string
|
||
|
n = bytes.Index([]byte(s.cs.AgentID), []byte{0})
|
||
|
if n != -1 {
|
||
|
agentid = string([]byte(s.cs.AgentID)[:n])
|
||
|
} else {
|
||
|
agentid = s.cs.AgentID
|
||
|
}
|
||
|
|
||
|
n = bytes.Index([]byte(s.cs.Station), []byte{0})
|
||
|
var sstation string
|
||
|
if n != -1 {
|
||
|
sstation = string([]byte(s.cs.Station)[:n])
|
||
|
} else {
|
||
|
sstation = s.cs.Station
|
||
|
}
|
||
|
|
||
|
timei := time.Now().UnixNano()
|
||
|
if len(s.callId) == 0 {
|
||
|
s.callId = fmt.Sprintf("%d@%s", timei, agentid)
|
||
|
}
|
||
|
|
||
|
//var processResp *icshttpclient.ProcessResp
|
||
|
if strings.Compare(s.botStatus.Action, "END") != 0 {
|
||
|
fmt.Println(url, s.botStatus.Action, "", custid, "ICS_RCP", s.callId, s.botToken, "")
|
||
|
processResp = icshttpclient.PostProcess(url, s.botStatus.Action, result, custid, "ICS_RCP", s.callId, sstation, s.botToken, "")
|
||
|
}
|
||
|
//processResp := icshttpclient.PostProcess(url, "INIT", result, custid, "ICS_RCP", s.callId, "", filename)
|
||
|
if processResp == nil {
|
||
|
l.Print(icslog.LOG_LEVEL_ERROR, s.ID, "Response Error")
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break // 특정 session에 대해서 처리하는 부분이라 break가 맞을거같음, 그래야 아랫쪽에서도 문제가 안생김
|
||
|
} else {
|
||
|
if processResp.ResultCode == 200 {
|
||
|
s.botStatus = processResp
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Response : %+v", processResp)
|
||
|
|
||
|
if strings.Compare(s.botStatus.Action, "END") != 0 {
|
||
|
fmt.Println(">>> ment", processResp.AnnounceMents)
|
||
|
//tts
|
||
|
pcm, ttsErr := tts.TTS(processResp.AnnounceMents, 8000)
|
||
|
if ttsErr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "TTS Error[%s] - %s",
|
||
|
processResp.AnnounceMents, ttsErr.GetError())
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
} else {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "%s!!! TTS Result: [%s]%d bytes",
|
||
|
agentname, processResp.AnnounceMents, len(pcm))
|
||
|
|
||
|
//send tts data to voice gateway
|
||
|
r := responsecs.NewResponse(agentname, responsecs.TTS_COMMAND, responsecs.RC_SUCCESS, len(pcm))
|
||
|
addr := icsnet.NewNetAddrWithIPPort(conf.VGWCommandConfig.IP, conf.VGWCommandConfig.Port)
|
||
|
t, wlen, werr := icsnet.SendCallSignal2(&addr, nil, r.GetData())
|
||
|
//wlen, werr := s.TcpConn.Write(r.GetData())
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS header - %s [%+v]",
|
||
|
werr.GetError(), s.TcpConn)
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent TTS header : %d", wlen)
|
||
|
|
||
|
s.TcpConn = t
|
||
|
wlen, werr = s.TcpConn.Write(pcm)
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS data - %s",
|
||
|
werr.GetError())
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent TTS data : %d", wlen)
|
||
|
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Barge-In: %s", processResp.Data.BargeIn)
|
||
|
switch processResp.Data.BargeIn {
|
||
|
case "Y":
|
||
|
//s.StartSTT(wlen)
|
||
|
s.isSTTStart = true
|
||
|
case "N":
|
||
|
s.StartSTT(wlen)
|
||
|
//s.isSTTStart = false
|
||
|
}
|
||
|
}
|
||
|
} else { //end
|
||
|
fmt.Println(">>> ment", processResp.AnnounceMents)
|
||
|
//tts
|
||
|
pcm, ttsErr := tts.TTS(processResp.AnnounceMents, 8000)
|
||
|
if ttsErr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "TTS Error[%s] - %s",
|
||
|
processResp.AnnounceMents, ttsErr.GetError())
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
} else {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "%s!!! TTS Result: [%s]%d bytes",
|
||
|
agentname, processResp.AnnounceMents, len(pcm))
|
||
|
|
||
|
//send command BYE to voice gateway
|
||
|
//send tts data to voice gateway
|
||
|
r := responsecs.NewResponse(agentname, responsecs.BYE_COMMAND, responsecs.RC_SUCCESS, len(pcm))
|
||
|
addr := icsnet.NewNetAddrWithIPPort(conf.VGWCommandConfig.IP, conf.VGWCommandConfig.Port)
|
||
|
t, wlen, werr := icsnet.SendCallSignal2(&addr, nil, r.GetData())
|
||
|
//wlen, werr := s.TcpConn.Write(r.GetData())
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS header - %s [%+v]",
|
||
|
werr.GetError(), s.TcpConn)
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
}
|
||
|
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Barge-In: %s", processResp.Data.BargeIn)
|
||
|
switch processResp.Data.BargeIn {
|
||
|
case "Y":
|
||
|
//s.StartSTT(wlen)
|
||
|
s.isSTTStart = true
|
||
|
case "N":
|
||
|
s.StartSTT(wlen)
|
||
|
//s.isSTTStart = false
|
||
|
}
|
||
|
|
||
|
s.TcpConn = t
|
||
|
wlen, werr = s.TcpConn.Write(pcm)
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS data - %s",
|
||
|
werr.GetError())
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent TTS data : %d", wlen)
|
||
|
}
|
||
|
//send command BYE to voice gateway
|
||
|
//r := responsecs.NewResponse(agentname, responsecs.BYE_COMMAND, responsecs.RC_SUCESS, 0)
|
||
|
//addr := icsnet.NewNetAddrWithIPPort(conf.VGWCommandConfig.IP, conf.VGWCommandConfig.Port)
|
||
|
//_, wlen, werr := icsnet.SendCallSignal2(&addr, nil, r.GetData())
|
||
|
//if werr != nil {
|
||
|
//l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send BYE command - %s [%+v]",
|
||
|
//werr.GetError(), s.TcpConn)
|
||
|
//continue
|
||
|
//}
|
||
|
//l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent BYE command : %d", wlen)
|
||
|
|
||
|
}
|
||
|
} else {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Response Error : %+v", processResp)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
********************************/
|
||
|
case "voicecapture":
|
||
|
//TODO: websocket
|
||
|
//fmt.Println("RX RESULT>>>", result, rms)
|
||
|
s.rxSeq++
|
||
|
imsg := icsws.NewIAP()
|
||
|
imsg.SendMessage("RX", agentname, result, s.rxSeq)
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Send Message: %+v", imsg)
|
||
|
s.WSConn.Write(imsg.String())
|
||
|
}
|
||
|
|
||
|
//voice save yn
|
||
|
if conf.VoiceConfig.SaveYn == "N" {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "[%d]RX Recog Result> %s, PCM(%f), BufLen(%d)",
|
||
|
s.goroutineID, result, rmsval, size)
|
||
|
} else {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "[%d]RX Recog Result> %s, PCM(%f), BufLen(%d) %s",
|
||
|
s.goroutineID, result, rmsval, size, filename)
|
||
|
}
|
||
|
} else if err == icserror.ICSERRSTTFail || err == icserror.ICSERRSTTFailEmpty {
|
||
|
l.Printf(icslog.LOG_LEVEL_WARN, s.ID, "Rx STT Failed - %s", err.GetError())
|
||
|
s.isSTTStart = false
|
||
|
|
||
|
//if err == icserror.ICSERRSTTFail {
|
||
|
uniq = fmt.Sprintf("%s-RX-Fail-", s.cs.Station)
|
||
|
filename := s.STTRx.Save(uniq, conf.VoiceConfig.Path)
|
||
|
s.STTRx.Close()
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "RX Recog Failed> %s-%v, PCM(%f), %s", err.GetMessage(), err.GetError(), rmsval, filename)
|
||
|
|
||
|
switch s.AgentConfig.Action {
|
||
|
case "voicegateway":
|
||
|
sttResult := stt.NewSTTResult(result, err)
|
||
|
////////////////////////////////////////////////
|
||
|
//post event to the session
|
||
|
h := icsevent.NewEventH()
|
||
|
var evt *icsevent.Event
|
||
|
var evtErr *icserror.IcsError
|
||
|
if evt, evtErr = h.AllocEvent(sttResult); evtErr != nil {
|
||
|
l.Print(icslog.LOG_LEVEL_ERROR, s.ID, "Response Error")
|
||
|
s.SendVoiceGatewayByeSignal()
|
||
|
continue
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, -1, "Post STT Result[%d] to Session[%03d]", evt.ID, s.ID)
|
||
|
h.PostEvent(s.ID, evt)
|
||
|
|
||
|
/**********************
|
||
|
url = fmt.Sprintf("http://%s:%d%s",
|
||
|
s.AgentConfig.BotConfig.IP,
|
||
|
s.AgentConfig.BotConfig.Port,
|
||
|
s.AgentConfig.BotConfig.URL2)
|
||
|
//remove null terminator
|
||
|
var custid, agentid string
|
||
|
n := bytes.Index([]byte(s.cs.CustID), []byte{0})
|
||
|
if n != -1 {
|
||
|
custid = string([]byte(s.cs.CustID)[:n])
|
||
|
} else {
|
||
|
custid = s.cs.CustID
|
||
|
}
|
||
|
n = bytes.Index([]byte(s.cs.AgentID), []byte{0})
|
||
|
if n != -1 {
|
||
|
agentid = string([]byte(s.cs.AgentID)[:n])
|
||
|
} else {
|
||
|
agentid = s.cs.AgentID
|
||
|
}
|
||
|
|
||
|
n = bytes.Index([]byte(s.cs.Station), []byte{0})
|
||
|
var sstation string
|
||
|
if n != -1 {
|
||
|
sstation = string([]byte(s.cs.Station)[:n])
|
||
|
} else {
|
||
|
sstation = s.cs.Station
|
||
|
}
|
||
|
|
||
|
timei := time.Now().UnixNano()
|
||
|
if len(s.callId) == 0 {
|
||
|
s.callId = fmt.Sprintf("%d@%s", timei, agentid)
|
||
|
}
|
||
|
|
||
|
fmt.Println(url, "STT_FAIL", "", custid, "ICS_RCP", s.callId, s.botToken, "")
|
||
|
|
||
|
processResp = icshttpclient.PostProcess(url, "STT_FAIL", "", custid, "ICS_RCP", s.callId, sstation, s.botToken, "")
|
||
|
s.botStatus = processResp
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "processResp - %+v", processResp)
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
|
||
|
if strings.Compare(s.botStatus.Action, "END") != 0 {
|
||
|
fmt.Println(">>> ment", processResp.AnnounceMents)
|
||
|
//tts
|
||
|
pcm, ttsErr := tts.TTS(processResp.AnnounceMents, 8000)
|
||
|
if ttsErr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "TTS Error[%s] - %s",
|
||
|
processResp.AnnounceMents, ttsErr.GetError())
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
} else {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "%s!!! TTS Result: [%s]%d bytes",
|
||
|
agentname, processResp.AnnounceMents, len(pcm))
|
||
|
|
||
|
//send tts data to voice gateway
|
||
|
r := responsecs.NewResponse(agentname, responsecs.TTS_COMMAND, responsecs.RC_SUCCESS, len(pcm))
|
||
|
addr := icsnet.NewNetAddrWithIPPort(conf.VGWCommandConfig.IP, conf.VGWCommandConfig.Port)
|
||
|
t, wlen, werr := icsnet.SendCallSignal2(&addr, nil, r.GetData())
|
||
|
//wlen, werr := s.TcpConn.Write(r.GetData())
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS header - %s [%+v]",
|
||
|
werr.GetError(), s.TcpConn)
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent TTS header : %d", wlen)
|
||
|
|
||
|
s.TcpConn = t
|
||
|
wlen, werr = s.TcpConn.Write(pcm)
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS data - %s",
|
||
|
werr.GetError())
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent TTS data : %d", wlen)
|
||
|
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Barge-In: %s", processResp.Data.BargeIn)
|
||
|
switch processResp.Data.BargeIn {
|
||
|
case "Y":
|
||
|
//s.StartSTT(wlen)
|
||
|
s.isSTTStart = true
|
||
|
case "N":
|
||
|
s.StartSTT(wlen)
|
||
|
//s.isSTTStart = false
|
||
|
}
|
||
|
}
|
||
|
} else { //end
|
||
|
fmt.Println(">>> ment", processResp.AnnounceMents)
|
||
|
//tts
|
||
|
pcm, ttsErr := tts.TTS(processResp.AnnounceMents, 8000)
|
||
|
if ttsErr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "TTS Error[%s] - %s",
|
||
|
processResp.AnnounceMents, ttsErr.GetError())
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
} else {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "%s!!! TTS Result: [%s]%d bytes",
|
||
|
agentname, processResp.AnnounceMents, len(pcm))
|
||
|
|
||
|
//send command BYE to voice gateway
|
||
|
//send tts data to voice gateway
|
||
|
r := responsecs.NewResponse(agentname, responsecs.BYE_COMMAND, responsecs.RC_SUCCESS, len(pcm))
|
||
|
addr := icsnet.NewNetAddrWithIPPort(conf.VGWCommandConfig.IP, conf.VGWCommandConfig.Port)
|
||
|
t, wlen, werr := icsnet.SendCallSignal2(&addr, nil, r.GetData())
|
||
|
//wlen, werr := s.TcpConn.Write(r.GetData())
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS header - %s [%+v]",
|
||
|
werr.GetError(), s.TcpConn)
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent BYE command header(%d)", wlen)
|
||
|
|
||
|
s.TcpConn = t
|
||
|
wlen, werr = s.TcpConn.Write(pcm)
|
||
|
if werr != nil {
|
||
|
l.Printf(icslog.LOG_LEVEL_ERROR, s.ID, "Failed to send TTS data - %s",
|
||
|
werr.GetError())
|
||
|
continue
|
||
|
// s.SendVoiceGatewayBotByeSignal(url, custid, sstation)
|
||
|
// break
|
||
|
}
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Sent TTS data : %d", wlen)
|
||
|
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "Barge-In: %s", processResp.Data.BargeIn)
|
||
|
switch processResp.Data.BargeIn {
|
||
|
case "Y":
|
||
|
//s.StartSTT(wlen)
|
||
|
s.isSTTStart = true
|
||
|
case "N":
|
||
|
s.StartSTT(wlen)
|
||
|
//s.isSTTStart = false
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
*********************************/
|
||
|
}
|
||
|
/*
|
||
|
} else { //empty activity voice
|
||
|
s.STTRx.Close()
|
||
|
//fmt.Printf("RX fail>> %+v, %f, %+v\n", result, rms, err)
|
||
|
//l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "RX Recog Failed(empty result)> %s-%v(%f)", err.GetMessage(), err.GetError(), rmsval)
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
case 1: //TX
|
||
|
uniq = fmt.Sprintf("%s-TX-", s.cs.Station)
|
||
|
//STT
|
||
|
//l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "%s\n%s", s.STTTx, s.STTRx)
|
||
|
result, rmsval, err := s.STTTx.STT(voicedata)
|
||
|
//l.Printf(icslog.LOG_LEVEL_DEBUG2, a.ID, "[%f]%+v\n", rmsval, voicedata)
|
||
|
if err == icserror.ICSERRSTTContinue {
|
||
|
//fmt.Printf("TX >>> %f, %s\n", rms, result)
|
||
|
continue
|
||
|
} else if err == icserror.ICSERRSTTOK {
|
||
|
filename := s.STTTx.Save(uniq, conf.VoiceConfig.Path)
|
||
|
size := s.STTTx.GetVoiceBufCur()
|
||
|
s.STTTx.Close()
|
||
|
|
||
|
//websocket
|
||
|
//fmt.Println("TX RESULT>>>", result, rms)
|
||
|
s.txSeq++
|
||
|
imsg := icsws.NewIAP()
|
||
|
imsg.SendMessage("TX", agentname, result, s.txSeq)
|
||
|
l.Printf(icslog.LOG_LEVEL_DEBUG2, s.ID, "Send Message: %+v", imsg)
|
||
|
s.WSConn.Write(imsg.String())
|
||
|
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "[%d]TX Recog Result> %s, PCM(%f), BufLen(%d) %s",
|
||
|
s.goroutineID, result, rmsval, size, filename)
|
||
|
} else if err == icserror.ICSERRSTTFail || err == icserror.ICSERRSTTFailEmpty {
|
||
|
l.Printf(icslog.LOG_LEVEL_WARN, s.ID, "Tx STT Failed - %s", err.GetError())
|
||
|
if err == icserror.ICSERRSTTFail {
|
||
|
uniq = fmt.Sprintf("%s-TX-Fail-", s.cs.Station)
|
||
|
filename := s.STTTx.Save(uniq, conf.VoiceConfig.Path)
|
||
|
|
||
|
//voice save
|
||
|
if conf.VoiceConfig.SaveYn == "N" {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "TX Recog Failed> %s-%v, PCM(%f)>",
|
||
|
err.GetMessage(), err.GetError(), rmsval)
|
||
|
} else {
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "TX Recog Failed> %s-%v, PCM(%f)> %s",
|
||
|
err.GetMessage(), err.GetError(), rmsval, filename)
|
||
|
}
|
||
|
|
||
|
s.STTTx.Close()
|
||
|
} else { //empty activity voice
|
||
|
s.STTTx.Close()
|
||
|
//fmt.Printf("TX fail>> %+v, %f, %+v\n", result, rms, err)
|
||
|
//l.Printf(icslog.LOG_LEVEL_INFO, a.ID, "TX Recog Failed> %s-%v(%f)", err.GetMessage(), err.GetError(), rmsval)
|
||
|
}
|
||
|
}
|
||
|
default:
|
||
|
fmt.Printf("Type: %T, value: %v\n", voicedata.Type, voicedata.Type)
|
||
|
}
|
||
|
}
|
||
|
//file.Close()
|
||
|
}()
|
||
|
|
||
|
wait.Wait()
|
||
|
|
||
|
l.Print(icslog.LOG_LEVEL_DEBUG2, s.ID, "Stoped RecvVoice")
|
||
|
}
|
||
|
|
||
|
func (s *IcsSession) StartSTT(length int) {
|
||
|
l := icslog.GetIcsLog()
|
||
|
fst := float32(length/16) * 0.6
|
||
|
//fst := float32(wlen/16) * 0.5
|
||
|
stttime := int64(fst)
|
||
|
//stttime := time.Duration(wlen / 16)
|
||
|
fmt.Println("STT timer", time.Duration(stttime), stttime, time.Now())
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "STT timer ", time.Duration(stttime), stttime, time.Now())
|
||
|
// STTStartTimer := time.NewTimer(time.Millisecond * time.Duration(stttime))
|
||
|
s.isSTTStartTimer = time.NewTimer(time.Millisecond * time.Duration(stttime))
|
||
|
//s.isSTTStartTimer = *time.NewTimer(time.Millisecond * time.Duration(wlen/16))
|
||
|
go func() {
|
||
|
// <-STTStartTimer.C
|
||
|
<-s.isSTTStartTimer.C
|
||
|
s.isSTTStart = true
|
||
|
// fmt.Println("STT START2", time.Now())
|
||
|
l.Printf(icslog.LOG_LEVEL_INFO, s.ID, "STT START2 ", time.Now())
|
||
|
}()
|
||
|
}
|