From 044ad18c172cfa9d656d2919b1c50ab7777ef47f Mon Sep 17 00:00:00 2001 From: jiyoungcheon Date: Thu, 28 Nov 2024 19:17:40 -0500 Subject: [PATCH] refactored --- go.mod | 2 +- icsbatch/batch_everyday.go | 95 +++- icsconf/icsconf.go | 14 +- icserror/icserrordef.go | 34 +- icshttp/client.go | 321 ++++++++++++ icshttp/http_request.go | 33 -- icslog/icslog.go | 2 +- icsstt/data.go | 4 +- icsstt/googlestt.go | 2 +- icsstt/stt_test.go | 6 +- icsstt/sttcontroller.go | 248 +++------ icsstt/sttselvas.go | 1008 ++++++++++++++++++++++++++++++++++++ icsutil/util.go | 125 +++++ main.go | 8 +- 14 files changed, 1647 insertions(+), 255 deletions(-) create mode 100644 icshttp/client.go delete mode 100644 icshttp/http_request.go create mode 100644 icsstt/sttselvas.go create mode 100644 icsutil/util.go diff --git a/go.mod b/go.mod index f748bd0..ce1e74c 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module mybatch +module batchmodule go 1.22.0 diff --git a/icsbatch/batch_everyday.go b/icsbatch/batch_everyday.go index 9082677..51b20c2 100644 --- a/icsbatch/batch_everyday.go +++ b/icsbatch/batch_everyday.go @@ -1,41 +1,114 @@ package icsbatch import ( + "batchmodule/icsconf" + "batchmodule/icshttp" + "batchmodule/icslog" + "batchmodule/icsstt" + "batchmodule/icsutil" "fmt" + "io" "log" - "mybatch/icsconf" - "mybatch/icslog" - stt "mybatch/icsstt" + "os" + "path/filepath" + "strconv" + "strings" "time" "github.com/robfig/cron/v3" ) var ( - cfg icsconf.AppInfo - l *log.Logger + cfg icsconf.AppInfo + l *log.Logger + fList icshttp.FailedList ) +type FailedList struct { + No int `json:"no"` + ConnId string `json:"connid"` + Ext string `json:"ext"` + StartTime string `json:"starttime"` +} + func init() { cfg = icsconf.Getconfig() l = icslog.InitializeLogger() } -func BatchForPostRequestForDataList() { +func BatchForFailedSTT() { + wavFilePath := cfg.Directories.WAVDirectory loc, err := time.LoadLocation("Asia/Seoul") if err != nil { l.Fatalln("타임존 로드 실패: ", err) } cronInstance := cron.New(cron.WithLocation(loc)) - // 매일 새벽 1시에 동작하는 크론인스턴스 추가 + // Adding Cron Instance which runs at every 1am _, err = cronInstance.AddFunc("0 1 * * *", func() { - l.Println("calling ProcessPostRequestForDataList()") - stt.ProcessPostRequestForDataList() + l.Println("cron is working...calling ProcessPostRequestForDataList()...at ", time.Now().Format("2006-01-02_15:04:05")) + + // request for failed datalist + response, err := icshttp.PostReqForFailedDataList() + if err != nil { + l.Printf("batch_everyday.go>BatchForFailedSTT> icshttp.PostReqForFailedDataList() failed. err: %+v", err) + } + totalCnt, _ := strconv.ParseInt(response.TotalCnt, 10, 64) + if int(totalCnt) <= 0 { + l.Printf("batch_everyday.go>BatchForFailedSTT> icshttp.PostReqForFailedDataList()> The Failed Data is Zero. totalCnt: %d", totalCnt) + } else { + for _, item := range response.List { + reqDataForVoiceFile := icshttp.FailedDataListReqBody{ + StartTime: item.StartTime, + Ext: item.Ext, + ConnId: item.ConnId, + } + // request for each voice file on the failed datalist + response, err := icshttp.PostReqForEachVoiceFile(reqDataForVoiceFile) + if err != nil { + l.Printf("batch_everyday.go>BatchForFailedSTT> icshttp.PostReqForEachVoiceFile() failed. err: %+v", err) + } + voiceFile := response.VoiceFile + if voiceFile == "" { + l.Printf("batch_everyday.go>BatchForFailedSTT> voiceFile is empty on %+v", reqDataForVoiceFile) + break + } + fileName := fmt.Sprintf(`%s/%s/%s.wav`, wavFilePath, item.StartTime, item.ConnId) + file, error := os.Create(fileName) + if error != nil { + l.Println("Error at batch_everyday.go>BatchForFailedSTT> os.Create() err: ", error) + break + } + defer file.Close() + voiceFileReader := strings.NewReader(voiceFile) + _, error = io.Copy(file, voiceFileReader) + if error != nil { + l.Println("Error at batch_everyday.go>BatchForFailedSTT> io.Copy(file, voiceFileReader) err: ", error) + break + } + + // devide the wav file to 2 channels + pcmResult, folderName := icsutil.DevideWavTo2Pcm(fileName, item.StartTime) + + if pcmResult { + // RUN STT + sttCallRes, err := icsstt.STTController(reqDataForVoiceFile, folderName) + if err != nil { + l.Println("runSTT() failed with the error: ", err) + } + // Delete the pcm files if stt was successful + if sttCallRes { + pcmDir := filepath.Join(cfg.Directories.PCMDirectory, st) + icsutil.DeletePcmFolder(pcmDir) + + } + } + } + } }) if err != nil { - fmt.Println("스케줄 추가 오류:", err) + l.Printf("Error at batch_everyday.go>BatchForFailedSTT> Adding cron instance failed. error: %v", err) return } cronInstance.Start() @@ -43,7 +116,7 @@ func BatchForPostRequestForDataList() { select {} } -func BatchForPostRequestForDataListTest() { +func BatchForFailedSTTTest() { loc, err := time.LoadLocation("Asia/Seoul") if err != nil { l.Fatalln("타임존 로드 실패: ", err) diff --git a/icsconf/icsconf.go b/icsconf/icsconf.go index 5a013bf..3af8900 100644 --- a/icsconf/icsconf.go +++ b/icsconf/icsconf.go @@ -12,6 +12,18 @@ type AppInfo struct { Speech Speech `xml:"Speech"` Directories Directories `xml:"Directories"` STT STTConf `xml:"STT"` + Urls Urls `xml:"Urls"` +} + +type Urls struct { + FailedDataListUrl string `xml:"FailedDataListUrl"` + NockChiServer NockChiServer `xml:"NockChiServer"` + TAUrl string `xml:"TAUrl"` +} + +type NockChiServer struct { + Port string `xml:"port,attr"` + SrcIP string `xml:"srcIP,attr"` } type Speech struct { @@ -39,7 +51,7 @@ var ( ) func loadConfig() { - file, err := os.Open("/home/prac/svc/icsbc/conf/Appinfo.xml") + file, err := os.Open("/home/jiyoungc/svc/icsbc/config/Appinfo.xml") if err != nil { fmt.Println("failed to open config file: ", err) return diff --git a/icserror/icserrordef.go b/icserror/icserrordef.go index 74f02ea..fd6084a 100644 --- a/icserror/icserrordef.go +++ b/icserror/icserrordef.go @@ -37,6 +37,9 @@ const ( ICS_ERROR_STRING_CONV ICS_ERROR_DAEMONIZE ICS_ERROR_INVALID_DATA + ICS_ERROR_MARSHAL + ICS_ERROR_UNMARSHAL + ICS_ERROR_READ ) var ( @@ -49,6 +52,9 @@ var ( ICSERRStrConv = NewIcsError("Atoi Error", ICS_ERROR_STRING_CONV) ICSERRDeamonize = NewIcsError("Process Deamonize Error", ICS_ERROR_DAEMONIZE) ICSERRInvalidData = NewIcsError("There is not valid data", ICS_ERROR_INVALID_DATA) + ICSERRMarshal = NewIcsError("Json marshal Error", ICS_ERROR_MARSHAL) + ICSERRUnmarshal = NewIcsError("Json unmarshal Error", ICS_ERROR_UNMARSHAL) + ICSERRRead = NewIcsError("io read Error", ICS_ERROR_READ) ) // config error @@ -286,21 +292,25 @@ var ( // http client const ( - ICS_ERROR_HTTP_CLIENT_RESPONSE = iota + ICS_ERROR_HTTP_CLIENT - ICS_ERROR_HTTP_CLIENT_CREATE - ICS_ERROR_HTTP_CLIENT_MARSHAL - ICS_ERROR_HTTP_CLIENT_UNMARSHAL - ICS_ERROR_HTTP_CLIENT_EXCUTION - ICS_ERROR_HTTP_CLIENT_READBODY + ICS_ERROR_HTTP_CLIENT_RESPONSE = iota + ICS_ERROR_HTTP_CLIENT + ICS_ERROR_HTTP_CLIENT_RESPONSE_FAIL = iota + ICS_ERROR_HTTP_CLIENT + ICS_ERROR_HTTP_CLIENT_CREATE = iota + ICS_ERROR_HTTP_CLIENT + ICS_ERROR_HTTP_CLIENT_MARSHAL = iota + ICS_ERROR_HTTP_CLIENT + ICS_ERROR_HTTP_CLIENT_UNMARSHAL = iota + ICS_ERROR_HTTP_CLIENT + ICS_ERROR_HTTP_CLIENT_EXCUTION = iota + ICS_ERROR_HTTP_CLIENT + ICS_ERROR_HTTP_CLIENT_READBODY = iota + ICS_ERROR_HTTP_CLIENT + ICS_ERROR_HTTP_POST_REQUEST = iota + ICS_ERROR_HTTP_CLIENT ) var ( - ICSERRHTTPClientResponse = NewIcsError("icshttpclient: No Response", ICS_ERROR_HTTP_CLIENT_RESPONSE) - ICSERRHTTPClientCreate = NewIcsError("icshttpclient: http.NewRequest", ICS_ERROR_HTTP_CLIENT_CREATE) - ICSERRHTTPClientMarshal = NewIcsError("icshttpclient: Data Marshal", ICS_ERROR_HTTP_CLIENT_MARSHAL) - ICSERRHTTPClientUnmarshal = NewIcsError("icshttpclient: Data Unmarshal", ICS_ERROR_HTTP_CLIENT_UNMARSHAL) - ICSERRHTTPClientExcecution = NewIcsError("icshttpclient: Request http", ICS_ERROR_HTTP_CLIENT_EXCUTION) - ICSERRHTTPClientReadBody = NewIcsError("icshttpclient: Read Response Body", ICS_ERROR_HTTP_CLIENT_READBODY) + ICSERRHTTPClientResponse = NewIcsError("icshttpclient: No Response", ICS_ERROR_HTTP_CLIENT_RESPONSE) + ICSERRHTTPClientResponseFail = NewIcsError("icshttpclient: returned fail ", ICS_ERROR_HTTP_CLIENT_RESPONSE_FAIL) + ICSERRHTTPClientCreate = NewIcsError("icshttpclient: http.NewRequest", ICS_ERROR_HTTP_CLIENT_CREATE) + ICSERRHTTPClientMarshal = NewIcsError("icshttpclient: Data Marshal", ICS_ERROR_HTTP_CLIENT_MARSHAL) + ICSERRHTTPClientUnmarshal = NewIcsError("icshttpclient: Data Unmarshal", ICS_ERROR_HTTP_CLIENT_UNMARSHAL) + ICSERRHTTPClientExcecution = NewIcsError("icshttpclient: Request http", ICS_ERROR_HTTP_CLIENT_EXCUTION) + ICSERRHTTPClientReadBody = NewIcsError("icshttpclient: Read Response Body", ICS_ERROR_HTTP_CLIENT_READBODY) + ICSERRHTTPClientPostRequest = NewIcsError("icshttpclient: Post request failed", ICS_ERROR_HTTP_POST_REQUEST) ) // s3 diff --git a/icshttp/client.go b/icshttp/client.go new file mode 100644 index 0000000..aa214dd --- /dev/null +++ b/icshttp/client.go @@ -0,0 +1,321 @@ +package icshttp + +import ( + "batchmodule/icsconf" + "batchmodule/icserror" + "batchmodule/icslog" + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gorilla/websocket" +) + +var ( + conf icsconf.AppInfo + l *log.Logger +) + +func init() { + conf = icsconf.Getconfig() + l = icslog.InitializeLogger() +} + +type TAInfoJSON struct { + Cmd string `json:"cmd"` + ConnId string `json:"connId"` + Tel string `json:"tel"` + Ext string `json:"ext"` + EmpId string `json:"empId"` + DateTime string `json:"dateTime"` + CallStartTime string `json:"callStartTime"` + Index string `json:"index"` + Stt string `json:"stt"` + Dir string `json:"dir"` + IoGbn string `json:"ioGbn"` +} + +type FailedList struct { + No int `json:"no"` + ConnId string `json:"connid"` + Ext string `json:"ext"` + StartTime string `json:"starttime"` +} + +type FailedDataListReqBody struct { + StartTime string `json:"starttime"` + Ext string `json:"ext"` + ConnId string `json:"connid"` +} + +type FailedDataListResBody struct { + TotalCnt string `json:"totalCnt"` + ReturnCode string `json:"returnCode"` + ReturnMsg string `json:"returnMsg"` + List []FailedList `json:"list"` +} + +type FailedDataVoiceResBody struct { + VoiceFile string `json:"voiceFile"` + ReturnCode string `json:"returnCode"` + ReturnMsg string `json:"returnMsg"` +} + +type IcsHttpClient struct { + Method string + URL string + Request *http.Request + http.Client +} +type TAResp struct { + Success bool `json:"success"` + ReturnCode string `json:"returnCode"` + Message string `json:"message"` +} + +type NockChiReqBody struct { + Uid string `json:"uid"` + Ext string `json:"ext"` + SpeakerTag string `json:"speackertag"` + Transcripts string `json:"transcripts"` +} + +func NewIcsHttpClient(method, url string, body io.Reader) *IcsHttpClient { + c := &IcsHttpClient{Method: method, URL: url} + r, err := http.NewRequest(method, url, body) + if err != nil { + l.Println("HTTP Client error while http.NewRequest(). error: ", err) + return nil + } + c.Request = r + c.SetTimeout(time.Millisecond * 1000 * 5) + + return c +} + +func (c *IcsHttpClient) SetTimeout(timeout time.Duration) { + c.Timeout = timeout +} + +func HttpRequest(url, method, requestBody string) (*http.Response, error) { + // TLS 설정 + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 인증서 검증 비활성화 + } + client := &http.Client{Transport: tr} + + // client := &http.Client{} + payload := strings.NewReader(requestBody) + req, _ := http.NewRequest(method, url, payload) + req.Header.Set("Content-Type", "application/json") + + // 요청 + res, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("요청 오류: %w", err) + } + + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error: received non-200 response status: %s", res.Status) + } + + return res, nil +} + +func PostReqForFailedDataList() (response *FailedDataListResBody, cerr *icserror.IcsError) { + url := conf.Urls.FailedDataListUrl + currentDate := time.Now().Format("20060102") + currentDateUint64, _ := strconv.ParseUint(currentDate, 10, 64) + starttime := currentDateUint64 - 1 + starttimeStr := strconv.FormatUint(starttime, 10) + + body := FailedDataListReqBody{ + StartTime: starttimeStr, + Ext: "", + ConnId: "", + } + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, icserror.ICSERRMarshal + } + reqBody := bytes.NewBuffer(jsonBody) + + l.Printf("client.go>PostReqForFailedDataList()> url: %s, reqBody: %+v", url, reqBody.String()) + + client := NewIcsHttpClient("POST", url, reqBody) + if client == nil { + return nil, icserror.ICSERRHTTPClientCreate + } + client.Request.Header.Add("Content-Type", "application/json;charset=utf-8") + client.Request.Header.Add("Accept", "application/json") + + l.Printf("Error at client.go>PostReqForFailedDataList()> client.Request: %+v", client) + + resp, err := client.Do(client.Request) + if err != nil { + l.Printf("Err at client.go>PostReqForFailedDataList()> client.Do failed. resp: %+v", resp) + l.Printf("The err: %+v", err) + return nil, icserror.ICSERRHTTPClientExcecution + } + + l.Printf("client.go>PostReqForFailedDataList()> resp: %+v", resp) + resBody, err := io.ReadAll(resp.Body) + if err != nil { + l.Printf("client.go>PostReqForFailedDataList()> ReadAll err: %+v", err) + return nil, icserror.ICSERRRead + } + defer resp.Body.Close() + + if err := json.Unmarshal(resBody, &response); err != nil { + l.Printf("Error at client.go>PostReqForFailedDataList()> Unmarshal err: %+v", err) + return nil, icserror.ICSERRUnmarshal + } + if strings.Contains(response.ReturnCode, "9999") { + l.Printf("Error at client.go>PostReqForFailedDataList()> response returnCode is 9999 with the message: %s", response.ReturnMsg) + return nil, icserror.ICSERRHTTPClientResponseFail + } + + return response, nil + +} + +func PostReqForEachVoiceFile(reqDataForVoiceFile FailedDataListReqBody) (response *FailedDataVoiceResBody, cerr *icserror.IcsError) { + url := conf.Urls.FailedDataListUrl + jsonBody, err := json.Marshal(reqDataForVoiceFile) + if err != nil { + return nil, icserror.ICSERRMarshal + } + reqBody := bytes.NewBuffer(jsonBody) + l.Printf("client.go>PostReqForEachVoiceFile()> url: %s, reqBody: %+v", url, reqBody.String()) + client := NewIcsHttpClient("POST", url, reqBody) + if client == nil { + return nil, icserror.ICSERRHTTPClientCreate + } + client.Request.Header.Add("Content-Type", "application/json;charset=utf-8") + client.Request.Header.Add("Accept", "application/json") + + l.Printf("Error at client.go>PostReqForEachVoiceFile()> client.Request: %+v", client) + + resp, err := client.Do(client.Request) + if err != nil { + l.Printf("Error at client.go>PostReqForEachVoiceFile()> client.Do failed. resp: %+v", resp) + l.Printf("The err: %+v", err) + return nil, icserror.ICSERRHTTPClientExcecution + } + + l.Printf("client.go>PostReqForEachVoiceFile()> resp: %+v", resp) + resBody, err := io.ReadAll(resp.Body) + if err != nil { + l.Printf("client.go>PostReqForEachVoiceFile()> ReadAll err: %+v", err) + return nil, icserror.ICSERRRead + } + defer resp.Body.Close() + + if err := json.Unmarshal(resBody, &response); err != nil { + l.Printf("Error at client.go>PostReqForEachVoiceFile()> Unmarshal err: %+v", err) + return nil, icserror.ICSERRUnmarshal + } + if strings.Contains(response.ReturnCode, "9999") { + l.Printf("Error at client.go>PostReqForEachVoiceFile()> response returnCode is 9999 with the message: %s", response.ReturnMsg) + return nil, icserror.ICSERRHTTPClientResponseFail + } + l.Printf("client.go>PostReqForEachVoiceFile()> response: %+v", response) + + return response, nil + +} + +func SendSTTResToNockChiServer(parsedRes FailedDataListReqBody, sttRes string, dir string) *icserror.IcsError { + + url := conf.Urls.NockChiServer.SrcIP + headers := http.Header{ + "Origin": {"https://192.168.0.69"}, + } + conn, _, err := websocket.DefaultDialer.Dial(url, headers) + if err != nil { + l.Println("[ERR] client.go>SendSTTResToNockChiServer()> connecting websocket failed. err: %v", err) + return icserror.ICSERRWEBSOCKETConnectFailError + } + defer conn.Close() + + reqData := NockChiReqBody{ + Uid: parsedRes.ConnId, + Ext: parsedRes.Ext, + SpeakerTag: dir, + Transcripts: sttRes, + } + + jsonData, _ := json.Marshal(reqData) + + reqBody := fmt.Sprintf(`{"data":"%s"}`, jsonData) + + err = conn.WriteMessage(websocket.TextMessage, []byte(reqBody)) + if err != nil { + l.Println("[ERR] client.go>SendSTTResToNockChiServer()> conn.WriteMessage() failed. err: ", err) + return icserror.ICSERRWEBSOCKETWriteError + } + l.Println("[LOG] client.go>SendSTTResToNockChiServer()> the stt result(connId: %s) was successfully sent via websocket. reqBody: %s", parsedRes.ConnId, reqBody) + return nil + +} + +func PostProcessTA(url, cmd string, connId string, tel string, ext string, empId string, dateTime string, callStartTime string, index string, stt string, dir string, ioGbn string) (tAResp *TAResp, cerr *icserror.IcsError) { + + tAInfoJSON := TAInfoJSON{ + Cmd: cmd, + ConnId: connId, + Tel: tel, + Ext: ext, + EmpId: empId, + DateTime: dateTime, + CallStartTime: callStartTime, + Index: index, + Stt: stt, + Dir: dir, + IoGbn: ioGbn, + } + b, err := json.Marshal(tAInfoJSON) + if err != nil { + return nil, icserror.ICSERRMarshal + } + body := bytes.NewBuffer(b) + + client := NewIcsHttpClient("POST", url, body) + if client == nil { + l.Printf("[ERR] client.go>PostProcessTA()> NewIcsHttpClient() failed. err: %v", err) + l.Printf("[ERR] client.go>PostProcessTA()> NewIcsHttpClient() failed. url: %s, body: %s", url, body) + return nil, icserror.ICSERRHTTPClientCreate + } + client.Request.Header.Add("Content-Type", "application/json;charset=utf-8") + client.Request.Header.Add("Accept", "application/json") + l.Printf("[LOG] client.go>PostProcessTA()> Post Request Body: %+v", client.Request.Body) + + resp, err := client.Do(client.Request) + if err != nil { + l.Printf("[ERR] client.go>PostProcessTA()> client.Do failed. err: %v", err) + l.Printf("[ERR] client.go>PostProcessTA()> client.Do failed. resp: %v", resp) + return nil, icserror.ICSERRHTTPClientPostRequest + } + l.Printf("[LOG] client.go>PostProcessTA()> client.Do() resp: %v", resp) + + resBody, err := io.ReadAll(resp.Body) + if err != nil { + l.Printf("[ERR] client.go>PostProcessTA()> ReadAll failed. err: %v", err) + return nil, icserror.ICSERRRead + } + defer resp.Body.Close() + + if err := json.Unmarshal(resBody, &tAResp); err != nil { + l.Printf("[ERR] client.go>PostProcessTA()> Unmarshal failed. err: %v", err) + return nil, icserror.ICSERRUnmarshal + } + + return tAResp, nil +} diff --git a/icshttp/http_request.go b/icshttp/http_request.go deleted file mode 100644 index 5b34be9..0000000 --- a/icshttp/http_request.go +++ /dev/null @@ -1,33 +0,0 @@ -package httprequest - -import ( - "crypto/tls" - "fmt" - "net/http" - "strings" -) - -func HttpRequest(url, method, requestBody string) (*http.Response, error) { - // TLS 설정 - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 인증서 검증 비활성화 - } - client := &http.Client{Transport: tr} - - // client := &http.Client{} - payload := strings.NewReader(requestBody) - req, _ := http.NewRequest(method, url, payload) - req.Header.Set("Content-Type", "application/json") - - // 요청 - res, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("요청 오류: %w", err) - } - - if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("error: received non-200 response status: %s", res.Status) - } - - return res, nil -} diff --git a/icslog/icslog.go b/icslog/icslog.go index 0309832..056da8a 100644 --- a/icslog/icslog.go +++ b/icslog/icslog.go @@ -1,8 +1,8 @@ package icslog import ( + "batchmodule/icsconf" "log" - "mybatch/icsconf" "os" "path/filepath" "time" diff --git a/icsstt/data.go b/icsstt/data.go index 30ab0c0..51709be 100644 --- a/icsstt/data.go +++ b/icsstt/data.go @@ -1,10 +1,10 @@ -package stt +package icsstt import ( "math" "sync" - "mybatch/icslog" + "batchmodule/icslog" ) const RR_200MSEC_SIZE = 3200 diff --git a/icsstt/googlestt.go b/icsstt/googlestt.go index 7c7298a..c5aa48a 100644 --- a/icsstt/googlestt.go +++ b/icsstt/googlestt.go @@ -1,4 +1,4 @@ -package stt +package icsstt import ( "cloud.google.com/go/speech/apiv1" diff --git a/icsstt/stt_test.go b/icsstt/stt_test.go index cd320bb..54c8702 100644 --- a/icsstt/stt_test.go +++ b/icsstt/stt_test.go @@ -1,9 +1,9 @@ -package stt +package icsstt import ( + "batchmodule/icsconf" + "batchmodule/icserror" "fmt" - "mybatch/icsconf" - "mybatch/icserror" "os" "strconv" "testing" diff --git a/icsstt/sttcontroller.go b/icsstt/sttcontroller.go index 4959da6..ae357d0 100644 --- a/icsstt/sttcontroller.go +++ b/icsstt/sttcontroller.go @@ -1,4 +1,4 @@ -package stt +package icsstt import ( "encoding/binary" @@ -6,42 +6,18 @@ import ( "fmt" "io" "log" - "net/http" "os" "path/filepath" "strconv" "strings" - "time" - "mybatch/icsconf" - encry "mybatch/icsencry" - "mybatch/icserror" - httprequest "mybatch/icshttp" - "mybatch/icslog" - - "github.com/gorilla/websocket" + "batchmodule/icsconf" + encry "batchmodule/icsencry" + "batchmodule/icserror" + "batchmodule/icshttp" + "batchmodule/icslog" ) -type RequestData struct { - StartTime string `json:"starttime"` - Ext string `json:"ext"` - ConnID string `json:"connid"` -} - -type FileListResponse struct { - TotalCnt string `json:"totalCnt"` - ReturnCode string `json:"returnCode"` - ReturnMsg string `json:"returnMsg"` - List []FileInfo `json:"list"` -} - -type FileInfo struct { - No int `json:"no"` - ConnID string `json:"connid"` - Ext string `json:"ext"` - StartTime string `json:"starttime"` -} - type Transcript struct { Text string `json:"text"` StartTime int `json:"startTime"` @@ -69,52 +45,18 @@ type TAMessage struct { } var ( - cfg icsconf.AppInfo - l *log.Logger + conf icsconf.AppInfo + l *log.Logger + taUrl string ) func init() { - cfg = icsconf.Getconfig() + conf = icsconf.Getconfig() l = icslog.InitializeLogger() + taUrl = conf.Urls.TAUrl } func ProcessPostRequestForDataList() { - var starttime uint64 - currentDate := time.Now().Format("20060102") - currentDateUint64, _ := strconv.ParseUint(currentDate, 10, 64) - - starttime = currentDateUint64 - 1 - starttimeStr := strconv.FormatUint(starttime, 10) - - data := RequestData{ - StartTime: starttimeStr, - Ext: "", - ConnID: "", - } - - jsonData, _ := json.Marshal(data) - encryptedRequestBody := encry.Encrypting(jsonData) - - url := "https://192.168.0.69:8080/sttRest.do" - method := "POST" - requestBody := fmt.Sprintf(`{"data":"%s"}`, encryptedRequestBody) - - response, err := httprequest.HttpRequest(url, method, requestBody) - if err != nil { - l.Println("error occured while requesting http post for datalist:", err) - return - } - - defer response.Body.Close() - body, _ := io.ReadAll(response.Body) - - fmt.Println("응답 on ProcessPostRequestForDataList: ", string(body)) - - var parsedRes FileListResponse - if err := json.Unmarshal([]byte(body), &parsedRes); err != nil { - l.Println("Error parsing JSON:", err) - return - } var reqDataForVoiceFile RequestData if len(parsedRes.List) > 0 { @@ -140,14 +82,14 @@ func postReqForWavFileAndDownload(parsedRes RequestData) { method := "POST" requestBody := fmt.Sprintf(`{"data":"%s"}`, encryptedRequestBody) - response, err := httprequest.HttpRequest(url, method, requestBody) + response, err := icshttp.HttpRequest(url, method, requestBody) if err != nil { l.Println("error occured while requesting http post for datalist:", err) return } st := parsedRes.StartTime - wavFilePath := cfg.Directories.WAVDirectory - fileName := fmt.Sprintf(`%s/%s.wav`, wavFilePath, st) + wavFilePath := conf.Directories.WAVDirectory + fileName := fmt.Sprintf(`%s/%s/%s.wav`, wavFilePath, st, parsedRes.ConnId) file, err := os.Create(fileName) if err != nil { l.Println("error occured while creating file:", err) @@ -165,13 +107,13 @@ func postReqForWavFileAndDownload(parsedRes RequestData) { pcmResult, folderName := devideWavTo2Pcm(fileName) if pcmResult { - sttCallRes, err := controlSTT(parsedRes, folderName) + sttCallRes, err := ControlSTT(parsedRes, folderName) if err != nil { l.Println("runSTT() failed with the error: ", err) } // stt 콜이 성공이라면 pcm 파일 지우기 if sttCallRes { - pcmDir := filepath.Join(cfg.Directories.PCMDirectory, st) + pcmDir := filepath.Join(conf.Directories.PCMDirectory, st) deletePcmFolder(pcmDir) } @@ -199,7 +141,7 @@ func devideWavTo2Pcm(fileName string) (result bool, folderName string) { fileNameWithoutWav := fileName[:len(fileName)-4] lastSlashIdx := strings.LastIndex(fileNameWithoutWav, "/") starttime := fileNameWithoutWav[lastSlashIdx+1:] //starttime: 20250913223412 - pcmDir := cfg.Directories.PCMDirectory + pcmDir := conf.Directories.PCMDirectory starttimeDir := fmt.Sprintf("%s/%s", pcmDir, starttime) os.MkdirAll(starttimeDir, os.ModePerm) @@ -289,30 +231,28 @@ func devideWavTo2Pcm(fileName string) (result bool, folderName string) { return true, starttime } -// server 연결, stt 호출, 연결 해제 -func controlSTT(parsedRes RequestData, folderName string) (bool, *icserror.IcsError) { - pcmDirectory := cfg.Directories.PCMDirectory +func STTController(reqDataForVoiceFile icshttp.FailedDataListReqBody, folderName string) (bool, *icserror.IcsError) { + pcmDirectory := conf.Directories.PCMDirectory pcmDirectory = filepath.Join(pcmDirectory, folderName) // walking the file that has each left and right pcm files filepath.Walk(pcmDirectory, func(path string, info os.FileInfo, err error) error { if err != nil { - l.Println("Error occured while walking pcm folder. error: ", err) - l.Println("The pcm file path: ", pcmDirectory) + l.Printf("[ERR] sttcontroller>ControlSTT()> filepath.Walk() failed. pcmDirectory: %s", pcmDirectory) return err } if !info.IsDir() && filepath.Ext(path) == "left.pcm" { - stterr := connectSelvasServerRunSTT(parsedRes, path, "left") + stterr := connectSelvasServerRunSTT(reqDataForVoiceFile, path, "left") if stterr != nil { - l.Println("Error occured on recognizeSpeechFromPcmFile(). error: ", stterr) - return err + l.Println("[ERR] sttcontroller>ControlSTT()> connectSelvasServerRunSTT(). error: ", stterr) + return stterr } } if !info.IsDir() && filepath.Ext(path) == "right.pcm" { - stterr := connectSelvasServerRunSTT(parsedRes, path, "right") + stterr := connectSelvasServerRunSTT(reqDataForVoiceFile, path, "right") if stterr != nil { - l.Println("Error occured on recognizeSpeechFromPcmFile(). error: ", stterr) - return err + l.Println("[ERR] sttcontroller>ControlSTT()> connectSelvasServerRunSTT(). error: ", stterr) + return stterr } } return nil @@ -320,130 +260,66 @@ func controlSTT(parsedRes RequestData, folderName string) (bool, *icserror.IcsEr return true, nil } -func connectSelvasServerRunSTT(parsedRes RequestData, filePath string, leftright string) *icserror.IcsError { - var sttRes NewSTTResult - var sttErr *icserror.IcsError +func connectSelvasServerRunSTT(reqDataForVoiceFile icshttp.FailedDataListReqBody, filePath string, leftright string) *icserror.IcsError { voicedata, err := os.ReadFile(filePath) if err != nil { return icserror.ICSERRCONFOpenFile } - ip := cfg.STT.SrcIP - port, _ := strconv.Atoi(cfg.STT.Port) - callId := parsedRes.ConnID - custId := parsedRes.Ext - recordFilePath := cfg.Directories.RecDirectory + ip := conf.STT.SrcIP + port, _ := strconv.Atoi(conf.STT.Port) + + connId := reqDataForVoiceFile.ConnId + ext := reqDataForVoiceFile.Ext + recordFilePath := conf.Directories.RecDirectory + dir := "R" + if strings.Contains(leftright, "right") { + dir = "T" + } // connecting with STT server - s, sttErr := NewSTTS(ip, port, callId, custId, recordFilePath) + s, sttErr := NewSTTSshort(ip, port, connId, recordFilePath, isRx, true) if sttErr != nil { + l.Printf("sttcontroller>connectSelvasServerRunSTT> Failed to initialize Selvas STT connId(%s) - error: %s", connId, sttErr.GetMessage()) return icserror.ICSERRSTTConnectFail - } else if s != nil { - fmt.Println("STT session:", s) } - l.Println("STT server is connected with: ", ip, ", port: ", port) + l.Printf("sttcontroller>connectSelvasServerRunSTT> Initialized Selvas STT connId(%s) - %s:%d", connId, ip, port) // STT 호출 - sttRes, sttErr = s.SendSTT(voicedata, true, nil) + sttRes, sttErr := s.SendSTT(voicedata, true, nil) if sttErr != nil { - l.Println("calling sendSTT() on sttselvas.go has failed with error: ", sttErr) + l.Println("[ERR] sttcontroller>connectSelvasServerRunSTT> sendSTT() failed. error: ", sttErr) + s.Close() return icserror.ICSERRSTTSendFail } - // STT server and chanel close - s.CloseChanelAndServer() - - // STT 호출 성공시 결과를 websocket, ta rest로 송신 - if sttRes.Res != "" { - wssres, wsserr := webSocketSend(parsedRes, sttRes, leftright) - httpres, httperr := taRestSend(parsedRes, sttRes, s.GetReqTime(), leftright) - if wssres && httpres { - l.Println("stt results have successfully sent through both wss and http(ta)") - } - if wsserr != nil { - l.Println("sending stt result through websocket has failed with the error: ", wsserr) - } - if httperr != nil { - l.Println("sending stt result through http for ta has failed with the error: ", httperr) - } - } - return nil -} + l.Println("sttcontroller>connectSelvasServerRunSTT> sttRes: ", sttRes) -func webSocketSend(parsedRes RequestData, sttRes NewSTTResult, leftright string) (bool, *icserror.IcsError) { - speaker := "X" // T: 상담사 - if leftright == "left" { - speaker = "R" //R: 고객 - } - url := "ws://192.168.0.69:8090/wss" - headers := http.Header{ - "Origin": {"https://192.168.0.69"}, - } - conn, _, err := websocket.DefaultDialer.Dial(url, headers) + finalSTTRes, err := s.SendSTTProcGetResult() if err != nil { - l.Println("Failed to connect to WebSocket: %v", err) - return false, icserror.ICSERRWEBSOCKETConnectFailError + l.Printf("[ERR] sttcontroller>connectSelvasServerRunSTT> SendSTTProcGetResult() Failed. error: %v", err) } - defer conn.Close() - - resData := ResponseData{ - Uid: parsedRes.ConnID, - Ext: parsedRes.Ext, - SpeakerTag: speaker, - Transcripts: []Transcript{ - { - Text: sttRes.Res, - StartTime: sttRes.NStart, - EndTime: sttRes.NEnd, - EndPoint: true, - }, - }, - } - - jsonData, _ := json.Marshal(resData) - encryptedResponseBody := encry.Encrypting(jsonData) - responseBody := fmt.Sprintf(`{"data":"%s"}`, encryptedResponseBody) - - err = conn.WriteMessage(websocket.TextMessage, []byte(responseBody)) + // STT server and chanel close + err = s.Close() if err != nil { - l.Println("failed to send msg via websocket with the err: ", err) - return false, icserror.ICSERRWEBSOCKETWriteError - } else { - l.Println("Sent the msg via websocket. message body: ", responseBody) - return true, nil + l.Println("[ERR] sttcontroller>connectSelvasServerRunSTT> s.Close() failed. err: %v", err) } -} -func taRestSend(parsedRes RequestData, sttRes NewSTTResult, reqTime time.Time, leftright string) (bool, *icserror.IcsError) { - dir := "T" // T: 상담사 - if leftright == "left" { - dir = "R" //R: 고객 - } - resData := TAMessage{ - Cmd: "T", - ConnID: parsedRes.ConnID, - Tel: "", // 정보없음 - Ext: parsedRes.Ext, - CallStartTime: parsedRes.StartTime, - IgGbn: "1", // 인바운드 - DateTime: reqTime.Format("2006-01-02 15:04:05"), - Stt: sttRes.Res, - Dir: dir, - } - jsonData, _ := json.Marshal(resData) - encryptedRequestBody := encry.Encrypting(jsonData) + // STT 호출 성공시 결과를 websocket, ta로 송신 + if finalSTTRes != "" { + l.Printf("[DEBUG] sttcontroller>connectSelvasServerRunSTT> taUrl: %s", taUrl) - url := "https://192.168.0.69:8080/taRest.do" - method := "POST" - requestBody := fmt.Sprintf(`{"data":"%s"}`, encryptedRequestBody) + _, err := icshttp.PostProcessTA(taUrl, "E", connId, "", ext, "empIdSample", "", "", "0", finalSTTRes, dir, "1") + if err != nil { + l.Printf("[ERR] sttcontroller>connectSelvasServerRunSTT> PostProcessTA() got error : %v", err) + } - response, err := httprequest.HttpRequest(url, method, requestBody) - if err != nil { - l.Println("error occured while requesting http post for datalist:", err) - return false, icserror.ICSERRHTTPClientExcecution - } + err = icshttp.SendSTTResToNockChiServer(reqDataForVoiceFile, finalSTTRes, dir) + if err != nil { + l.Printf("[ERR] sttcontroller>connectSelvasServerRunSTT> SendSTTResToNockChiServer() got error : %v", err) + } - l.Println("TA msg has been successfully sent. response: ", response) - return true, nil + } + return nil } diff --git a/icsstt/sttselvas.go b/icsstt/sttselvas.go new file mode 100644 index 0000000..96314c7 --- /dev/null +++ b/icsstt/sttselvas.go @@ -0,0 +1,1008 @@ +// go:build !test +//go:build !test +// +build !test + +// for STT SELVAS STT +package icsstt + +/* +#cgo LDFLAGS: -lstdc++ -ldl ./extlib/selvasstt/SDK/LIB/c_linux/x64/libSTTLIB.a ./extlib/selvasstt/SDK/OpenSSL/Linux/Linux_x64/libssl.a ./extlib/selvasstt/SDK/OpenSSL/Linux/Linux_x64/libcrypto.a +#cgo CFLAGS: -I ../extlib/selvasstt/SDK/INCLUDE +#include +#include +#include +#include +#include + +char* getResultData(LVCSR_DATA_RESULT* pDataResult, int nCount) { + char* skip1 = ""; + char* space = " "; + int len = 0; + int i; + + for (i = 0; i < nCount; i++) { + int maxLength = 50 + strlen(pDataResult[i].pTokenStr); + char* strResult = malloc(sizeof(char) * maxLength); + sprintf(strResult, "%u|%u|%s\n", pDataResult[i].nStart, pDataResult[i].nEnd, pDataResult[i].pTokenStr); + + len += strlen(strResult) + 1; + printf("%ld|%ld\n", pDataResult[i].nStart, pDataResult[i].nEnd); + free(strResult); + } + + char* result = malloc(sizeof(char) * (len + 50)); + strcpy(result, ""); + + for (i = 0; i < nCount; i++) { + int maxLength = 50 + strlen(pDataResult[i].pTokenStr); + char* strResult = malloc(sizeof(char) * maxLength); + sprintf(strResult, "%u|%u|%s\n", pDataResult[i].nStart, pDataResult[i].nEnd, pDataResult[i].pTokenStr); + + if (strcmp(pDataResult[i].pTokenStr,skip1) == 0) { + } else { + strcat(result, space); + strcat(result, strResult); + } + free(strResult); + } + + return result; +} + +// char* getResultStrData(LVCSR_DATA_RESULT* pResultStr) { +// char* skip1 = ""; +// char* space = " "; +// int len = 0; +// long long i; + +// for (i = 0; i < nCount; i++) { +// len += strlen(pDataResult[i].pTokenStr) + 1; +// printf("start: %ld, end: %ld\n", pDataResult[i].nStart, pDataResult[i].nEnd); +// } + +// char* result = malloc(sizeof(char) * len); +// strcpy(result, ""); + +// for (i = 0; i < nCount; i++) { +// if (strcmp(pDataResult[i].pTokenStr,skip1) == 0) { +// } else { +// strcat(result, space); +// strcat(result, pDataResult[i].pTokenStr); +// } +// } + +// return result; +// } + +void freeResult(char* result){ + if(result == NULL) { + return; + } + free(result); +} + +int getMidResultEPD(LVCSR_RECOG_MID_RESULT *midResult){ + return midResult->bEngineDetectionFlag; +} + +void printMidResult(LVCSR_RECOG_MID_RESULT *midResult){ + printf("result len : %ld\n", midResult->nResultLen); + printf("pResultStr : %s\n", midResult->pResultStr); + printf("flag : %d\n", midResult->bEngineDetectionFlag); + printf("count: %ld\n\n", midResult->nDataCnt); +} + +void setSid(LVCSR_DATA_LOGINFO *logInfo, char* sid, int size) { + // memset(logInfo.tloItemSID, 0, 51); + // memcpy(logInfo.tloItemSID, sid, size); + sprintf(logInfo->tloItemSID, "%s", sid); + // printf("logInfo.tloItemSID-%s\n ", logInfo.tloItemSID); +} + +void setCallId(LVCSR_DATA_LOGINFO *logInfo, char* callId, int size) { + // memset(logInfo.tloItemCallID, 0, 61); + // memcpy(logInfo.tloItemCallID, callId, size); + sprintf(logInfo->tloItemCallID, "%s", callId); + // printf("logInfo.tloItemCallID-%s\n ", logInfo.tloItemSID); +} + +void setTrId(LVCSR_DATA_LOGINFO *logInfo, char* trId, int size) { + // memset(logInfo.tloItemTransactionID, 0, 101); + // memcpy(logInfo.tloItemTransactionID, trId, size); + sprintf(logInfo->tloItemTransactionID, "%s", trId); + // printf("logInfo.tloItemTransactionID-%s\n ", logInfo.tloItemTransactionID); +} + +void setItemStartMessage(LVCSR_DATA_LOGINFO *logInfo, char* tloTime, int size) { + // memset(logInfo.tloItemStartMessage, 0, 101); + // memcpy(logInfo.tloItemStartMessage, trId, size); + sprintf(logInfo->tloItemStartMessage, "%s", tloTime); + // printf("logInfo.tloItemStartMessage-%s\n ", logInfo.tloItemStartMessage); +} + +void setConnId(LVCSR_DATA_CALLINFO *callInfo, char* sid, int size) { + callInfo->pConnIdStr = (char*)malloc(size + 1); + memset(callInfo->pConnIdStr, 0, size + 1); + callInfo->nConnIdLen = size; + sprintf(callInfo->pConnIdStr, "%s", sid); + // printf("logInfo.tloItemSID-%s\n ", logInfo.tloItemSID); +} + +void setCallDateTime(LVCSR_DATA_CALLINFO *callInfo, unsigned long callDatetime) { + // memset(logInfo.tloItemCallID, 0, 61); + // memcpy(logInfo.tloItemCallID, callId, size); + LVCSR_DATE_TIMESTAMP pCallDateTime = { 0 }; + unsigned long sBaseTimeMillis = time(NULL); + SelvySTT_GET_TIMESTAMP(sBaseTimeMillis, &pCallDateTime); + + callInfo->pCallDateTime = pCallDateTime; + //sprintf(logInfo->tloItemCallID, "%s", callId); + // printf("logInfo.tloItemCallID-%s\n ", logInfo.tloItemSID); +} + +void freeConnId(LVCSR_DATA_CALLINFO *callInfo) { + if (callInfo->pConnIdStr){ + free(callInfo->pConnIdStr); + callInfo->pConnIdStr = NULL; + callInfo->nConnIdLen = 0; + } +} + +long getSTTSockID(LVCSR_SOCK_HEAD sockhead) { + return sockhead.nSock; +} + +*/ +import "C" +import ( + "fmt" + "log" + "runtime/debug" + "strings" + "sync" + "time" + "unsafe" + + // "git.icomsys.co.kr/icomsys/voicecapture/voiceagent/icsconf" + // "git.icomsys.co.kr/icomsys/voicecapture/voiceagent/icserror" + // "git.icomsys.co.kr/icomsys/voicecapture/voiceagent/icslog" + "batchmodule/icsconf" + "batchmodule/icserror" + "batchmodule/icslog" + + "github.com/google/uuid" +) + +const ( + CONNECT_TIMEOUT = 3 + READ_CONNECT_TIMEOUT = 5 + READ_CONNECT_TIMEOUT_LONGVOICE = 10 + + // model Info + MODEL_ID = 0 + KWD_ID = -1 + CODEC_TYPE = 0 // 8k + LANGUAGE = 1 // utf-8 + USED_EPD = 1 // epd used + ENGINE_EPD_USED_ON = 2 + NO_USED_EPD = 0 // epd used + //USED_EPD = 1 // epd used + USED_SCORE = 0 // used off + USED_ASYNC_MID_RESULT_OFF = 0 // mid result + USED_ASYNC_MID_RESULT_ON = 1 // mid result + DIRECTION_USED_UNK = 0 // Unknown + DIRECTION_USED_INB = 1 // Inbound + DIRECTION_USED_OB = 2 // Outbound + SEG_USED_MONO = 0 // Rx/Tx + SEG_USED_RX = 1 // Receiver + SEG_USED_TX = 2 // Transmitter + SEG_USED_BOT = 3 +) + +type AsyncCBFunc func(sttResult string, epd int, inout bool) + +type STTSelvas struct { + sessionID int + //handle int + authCode string + //ch int + sttID C.long + text string + voiceBuf []byte + voiceBufCur int64 + silencenum int + validnum int64 //rms counter + uDataSize int + uEndOfSpeech int + STTInfo STTInfo + IsClose bool + SpeechStatus int + RecordFilePath string + RecordStart *bool + rrData *RRData + M *sync.Mutex + txNum int //number of sending stt audio packet + callID string + custID string + callExt string + sreserved string + trid string + errCode string + errMsg string + errFunName string + result string + reqTime time.Time + rspTime time.Time + svcReqTime time.Time + svcRspTime time.Time + sttIdx int + + sttStatus int + //icsstat.StatInfos +} + +type STTInfo struct { + LVCSR_SOCK_HEAD C.LVCSR_SOCK_HEAD + LVCSR_EPD_INFO C.LVCSR_EPD_INFO + LVCSR_DATA_AUTHENTICATION C.LVCSR_DATA_AUTHENTICATION + LVCSR_RECOG_RESULT C.LVCSR_RECOG_RESULT + LVCSR_DATA_RESULT C.LVCSR_DATA_RESULT + LVCSR_RECOG_MID_RESULT C.LVCSR_RECOG_MID_RESULT + LVCSR_DATA_INFO C.LVCSR_DATA_INFO + LVCSR_DATA_LOGINFO C.LVCSR_DATA_LOGINFO + LVCSR_ERROR_RESULT C.LVCSR_ERROR_RESULT + LVCSR_DATA_CALLINFO C.LVCSR_DATA_CALLINFO +} + +type STTSResult struct { + result string + error *icserror.IcsError +} + +var ( + conf icsconf.AppInfo + l *log.Logger +) + +func init() { + conf = icsconf.Getconfig() + l = icslog.InitializeLogger() +} + +func (s STTSelvas) GetTrID() string { + return s.trid +} + +// connect SELVAS STT Server +func NewSTTSshort(IP string, port int, connId string, filePath string, isRx bool, isInbound bool) (*STTSelvas, *icserror.IcsError) { + //func NewSTTS(sid int, IP string, port int, callID string, custID string, filePath string, sreserved string, statOK bool) (*STTSelvas, *icserror.IcsError) { + var derr *icserror.IcsError = nil + l.Println("NewSTTSshort() has started!") + + defer func() { + if err := recover(); err != nil { + switch v := err.(type) { + case error: + icserror.ICSERRSTTFailInit.SetError(v) + l.Printf("PANIC! %s\n%s\n", icserror.ICSERRSTTFailInit.GetError().Error(), string(debug.Stack())) + default: + l.Println(icserror.ICSERRSTTFailInit.GetError().Error()) + } + } + derr = icserror.ICSERRSTTFailInit + }() + + if len(IP) <= 0 || port <= 0 { + derr = icserror.ICSERRInvalidParam + return nil, derr + } + + // STT 실패 재처리 모듈이라 callId, custId 없어서 connId 넣음. + + stts := STTSelvas{authCode: "LGUPlusManager", uDataSize: 1600, uEndOfSpeech: 0, IsClose: false, SpeechStatus: 0, reqTime: time.Now(), svcReqTime: time.Time{}, svcRspTime: time.Time{}, callID: connId, custID: connId, result: "", errCode: "", errMsg: "", errFunName: ""} + //stts := STTSelvas{handle: -1, authCode: "LGUPlusManager", uDataSize: 1600, uEndOfSpeech: 0, IsClose: false} + + stts.M = &sync.Mutex{} + stts.txNum = 0 + + //recording + stts.RecordFilePath = filePath + stts.RecordStart = new(bool) + *stts.RecordStart = true + stts.rrData = NewRRData(960000) //60sec + + csIP := C.CString(IP) + defer C.free(unsafe.Pointer(csIP)) + csPort := C.int(port) + csConTimeout := C.int(CONNECT_TIMEOUT) + csReadTimeout := C.int(READ_CONNECT_TIMEOUT) + + trID := uuid.New().String() + trID = strings.ReplaceAll(trID, "-", "a") + stts.trid = trID + + /* + if stts.sttStatus == STTMemo { + return &stts, nil + } + */ + + //svcReqTime := time.Now() + l.Println("STT SelvySTT_INIT Start") + rc := C.SelvySTT_INIT(csIP, csPort, csConTimeout, csReadTimeout, &stts.STTInfo.LVCSR_SOCK_HEAD) // add LVCSR_SOCK_HEAD + if int(rc) == -1 { + ////////////////테스트코드///////////////////// + rc = C.SelvySTT_GET_ERROR(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_ERROR_RESULT) + if int(rc) < 0 { + l.Printf("[%d] SelvySTT_GET_ERROR Fail.. %d\n", int(stts.sttID), rc) + // return "", derr + } else { + l.Printf("[%d] LVCSR_ERROR_RESULT - [%d]%s\n", int(stts.sttID), stts.STTInfo.LVCSR_ERROR_RESULT.nErrorCode, C.GoString(&stts.STTInfo.LVCSR_ERROR_RESULT.pErrorMsg[0])) + } + l.Printf("STT SelvySTT_INIT Fail - rc:%d\n", rc) + derr = icserror.ICSERRSTTConnectTimeout + return nil, derr + } else if int(rc) == -2 { + l.Printf("STT SelvySTT_INIT Fail - rc:%d\n", rc) + ////////////////테스트코드///////////////////// + rc = C.SelvySTT_GET_ERROR(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_ERROR_RESULT) + if int(rc) < 0 { + l.Printf("[%d] SelvySTT_GET_ERROR Fail.. %d\n", int(stts.sttID), rc) + // return "", derr + } else { + l.Printf("[%d] LVCSR_ERROR_RESULT - [%d]%s \n", int(stts.sttID), stts.STTInfo.LVCSR_ERROR_RESULT.nErrorCode, C.GoString(&stts.STTInfo.LVCSR_ERROR_RESULT.pErrorMsg[0])) + } + /////////////////////////////////////////////// + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + // callID = strings.ReplaceAll(callID, "-", "") + // if len(callID) > 12 { + // callID = callID[:12] + // } + + // l.Printf("callID: %s\n", callID) + + // if len(callID) == 0 { + // callID = trID + // } + + now := time.Now() + tloTime := fmt.Sprintf("%d%02d%02d%02d%02d%02d\n", now.Year(), int(now.Month()), now.Day(), now.Hour(), now.Minute(), now.Minute()) + csTrId := C.CString(trID) + defer C.free(unsafe.Pointer(csTrId)) + csConnId := C.CString(connId) + defer C.free(unsafe.Pointer(csConnId)) + csTloTime := C.CString(tloTime) + defer C.free(unsafe.Pointer(csTloTime)) + // csCustID := C.CString(custID) + csCustID := C.CString(connId) + defer C.free(unsafe.Pointer(csCustID)) + + // connid 없어서 callid로 connid 만들었던 코드. 해당파일에서는 connId 넣어줌 + // C.setConnId(&stts.STTInfo.LVCSR_DATA_CALLINFO, csCallId, C.int(len(callID))) + C.setConnId(&stts.STTInfo.LVCSR_DATA_CALLINFO, csConnId, C.int(len(connId))) + C.setCallDateTime(&stts.STTInfo.LVCSR_DATA_CALLINFO, C.ulong(time.Now().UnixNano())) + if isInbound { + stts.STTInfo.LVCSR_DATA_CALLINFO.nCallDirection = DIRECTION_USED_INB + } else { + stts.STTInfo.LVCSR_DATA_CALLINFO.nCallDirection = DIRECTION_USED_OB + } + if isRx { + stts.STTInfo.LVCSR_DATA_CALLINFO.nSpeakerSegmentation = SEG_USED_RX + } else { + stts.STTInfo.LVCSR_DATA_CALLINFO.nSpeakerSegmentation = SEG_USED_TX + } + + l.Printf("[%d] SET SelvySTT_SET_CALLINFO > connID:%s, direction:%d, speakerSeg:%d\n", int(stts.sttID), C.GoString(stts.STTInfo.LVCSR_DATA_CALLINFO.pConnIdStr), stts.STTInfo.LVCSR_DATA_CALLINFO.nCallDirection, stts.STTInfo.LVCSR_DATA_CALLINFO.nSpeakerSegmentation) + + // 콜인포 설정 + rs := C.SelvySTT_SET_CALLINFO(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_DATA_CALLINFO) + + // rs := C.SelvySTT_SET_LOGINFO(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_DATA_LOGINFO) + //ljhwan + //rs = 0 + if int(rs) < 0 { + l.Printf("[%d] STT SelvySTT_SET_LOGINFO Fail Result rs:%d, LVCSR_DATA_LOGINFO: %+v\n", int(stts.sttID), rs, stts.STTInfo.LVCSR_DATA_LOGINFO) + ////////////////테스트코드///////////////////// + rc = C.SelvySTT_GET_ERROR(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_ERROR_RESULT) + if int(rc) < 0 { + l.Printf("[%d] SelvySTT_GET_ERROR Fail..", int(stts.sttID)) + // return "", derr + } else { + l.Printf("[%d] LVCSR_ERROR_RESULT - [%d]%s\n", int(stts.sttID), stts.STTInfo.LVCSR_ERROR_RESULT.nErrorCode, C.GoString(&stts.STTInfo.LVCSR_ERROR_RESULT.pErrorMsg[0])) + } + /////////////////////////////////////////////// + + // Server Close + //svcReqTime = time.Now() + l.Printf("[%d] STT SelvySTT_EXIT Start\n", int(stts.sttID)) + rc = C.SelvySTT_EXIT(&stts.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Printf("[%d] STT SelvySTT_EXIT Fail Result rs:%d\n", int(stts.sttID), rs) + + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + C.freeConnId(&stts.STTInfo.LVCSR_DATA_CALLINFO) + + //svcReqTime = time.Now() + // Set Model List + stts.STTInfo.LVCSR_DATA_INFO.nModelID = MODEL_ID + stts.STTInfo.LVCSR_DATA_INFO.nKwdID = KWD_ID + stts.STTInfo.LVCSR_DATA_INFO.nCodecType = CODEC_TYPE + stts.STTInfo.LVCSR_DATA_INFO.nCharSet = LANGUAGE + // if stts.sttStatus == STTMemo { + // stts.STTInfo.LVCSR_DATA_INFO.nEpdUsed = NO_USED_EPD + // } else { + stts.STTInfo.LVCSR_DATA_INFO.nEpdUsed = ENGINE_EPD_USED_ON + stts.STTInfo.LVCSR_DATA_INFO.bAsyncMidRstUsed = USED_ASYNC_MID_RESULT_ON + // } + stts.STTInfo.LVCSR_DATA_INFO.bScoreUsed = USED_SCORE + + // Channel Connect + //svcReqTime = time.Now() + l.Printf("[%d] STT SelvySTT_OPEN Start\n", int(stts.sttID)) + rc = C.SelvySTT_OPEN(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_DATA_INFO) // add LVCSR_SOCK_HEAD + //ljhwan + //rc = 0 + if int(rc) < 0 { + l.Printf("[%d] STT SelvySTT_OPEN Fail, LVCSR_SOCK_HEAD: %+v\r\n", int(stts.sttID), stts.STTInfo.LVCSR_SOCK_HEAD) + // Server Close + //svcReqTime = time.Now() + l.Printf("[%d] STT SelvySTT_EXIT Start", int(stts.sttID)) + rc = C.SelvySTT_EXIT(&stts.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Printf("[%d]STT SelvySTT_EXIT Fail Result rs:%d, LVCSR_SOCK_HEAD: %+v\n", int(stts.sttID), rs, stts.STTInfo.LVCSR_SOCK_HEAD) + + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + // rc = C.SelvySTT_SET_INFO(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_DATA_INFO) + // //ljhwan + // //rc = 0 + // if int(rc) < 0 { + // l.Printf("[%d]STT SelvySTT_SET_INFO Fail Result rs:%d, LVCSR_SOCK_HEAD: %+v, stts.STTInfo.LVCSR_DATA_INFO: %+v\n", int(stts.sttID), rs, stts.STTInfo.LVCSR_SOCK_HEAD, stts.STTInfo.LVCSR_DATA_INFO) + + // // Channel Connection Close + // //svcReqTime = time.Now() + // l.Printf("[%d]STT SelvySTT_CLOS Start\n", int(stts.sttID)) + // rc = C.SelvySTT_CLOS(&stts.STTInfo.LVCSR_SOCK_HEAD) + // if int(rc) < 0 { + // l.Printf("[%d]STT SelvySTT_CLOS Fail Result rs:%d, LVCSR_SOCK_HEAD: %+v\n", int(stts.sttID), rs, stts.STTInfo.LVCSR_SOCK_HEAD) + // derr = icserror.ICSERRTTSNotInit + // } + + // // Server Close + // l.Printf("[%d]STT SelvySTT_EXIT Start\n", int(stts.sttID)) + // //svcReqTime = time.Now() + // rc = C.SelvySTT_EXIT(&stts.STTInfo.LVCSR_SOCK_HEAD) + // if int(rc) < 0 { + // l.Printf("[%d]STT SelvySTT_EXIT Fail Result rs:%d, LVCSR_SOCK_HEAD: %+v\n", int(stts.sttID), rs, stts.STTInfo.LVCSR_SOCK_HEAD) + // derr = icserror.ICSERRSTTFailInit + // return nil, derr + // } + // derr = icserror.ICSERRSTTFailInit + // return nil, derr + // } + + //l.Printf("[%d]Selvas STT New Connection > callID:'%s',trID:'%s'\n", int(stts.sttID), callID, trID) + l.Printf("[%d]Selvas STT New Connection > connId:'%s',trID:'%s'\n", int(stts.sttID), connId, trID) + return &stts, derr +} + +func (s *STTSelvas) Close() *icserror.IcsError { + if s == nil { + return icserror.ICSERRSTTNotInit + } + + var derr *icserror.IcsError = nil + + defer func() { + if err := recover(); err != nil { + switch v := err.(type) { + case error: + icserror.ICSERRSTTFailInit.SetError(v) + l.Printf("PANIC! %s\n%s\n", icserror.ICSERRSTTFailInit.GetError().Error(), string(debug.Stack())) + default: + l.Print(icserror.ICSERRSTTFailInit.GetError().Error()) + } + derr = icserror.ICSERRSTTFailInit + return + } + // derr = icserror.ICSERRSTTFailInit + }() + + l.Printf("Close STT[%d]. Send Audio Packet Num: %d\n", int(s.sttID), s.txNum) + + s.M.Lock() + s.IsClose = true + s.M.Unlock() + + *s.RecordStart = false + + rc := C.SelvySTT_CLS_RES(&s.STTInfo.LVCSR_RECOG_RESULT) + if int(rc) < 0 { + l.Printf("Failed to free STT result[%d]. rc: %d\n", int(s.sttID), rc) + if strings.Compare(s.errCode, "54000013") != 0 { + s.errCode, s.errMsg, s.errFunName = "54000010", "Fail", "SelvySTT_CLS_RES" + } + derr = icserror.ICSERRSTTFreeError + + } + + // Channel Connection Close + l.Printf("Close STT channel[%d]\n", int(s.sttID)) + rc = C.SelvySTT_CLOS(&s.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Printf("Failed to close STT channel[%d], rc: %d\n", int(s.sttID), rc) + if s.result == "$NO_RESULT$" { + s.errCode, s.errMsg, s.errFunName = "20000003", "Success", "" + } else if strings.Compare(s.errCode, "54000013") != 0 { + s.errCode, s.errMsg, s.errFunName = "54000011", "Fail", "SelvySTT_CLOS" + } + derr = icserror.ICSERRTTSNotInit + + } + + // Server Close + l.Printf("Close STT SVC[%d]\n", int(s.sttID)) + rc = C.SelvySTT_EXIT(&s.STTInfo.LVCSR_SOCK_HEAD) + //ljhwan + //rc = 0 + if int(rc) < 0 { + l.Printf("Failed to close STT SVC[%d], rc: %d\n", int(s.sttID), rc) + derr = icserror.ICSERRTTSNotInit + if strings.Compare(s.errCode, "54000013") != 0 { + s.errCode, s.errMsg, s.errFunName = "54000012", "Fail", "SelvySTT_EXIT" + } + + } + l.Printf("Closed STT[%d] sock header: %+v\n", int(s.sttID), s.STTInfo.LVCSR_SOCK_HEAD.nSock) + + s.rrData.Clear() + + return derr +} + +func (s *STTSelvas) GetSTTStatus() bool { + if s == nil { + // fmt.Println("get status: stt handle nil") + return false + } + s.M.Lock() + b := s.IsClose + s.M.Unlock() + + return b +} + +// type AsyncCBFunc func(sttResult string, epd int) +func (s *STTSelvas) SendSTT(voicedata []byte, final bool, cbFunc AsyncCBFunc) (string, *icserror.IcsError) { + + if s == nil { + return "", icserror.ICSERRTTSNotInit + } + + var result string + var sendCount int + var derr *icserror.IcsError = nil + var resultsWithTime string + + defer func() { + if err := recover(); err != nil { + switch v := err.(type) { + case error: + icserror.ICSERRSTTSendFail.SetError(v) + l.Printf("sttselvas.go>SendSTT()> PANIC! %s\n%s\n", icserror.ICSERRSTTSendFail.GetError().Error(), string(debug.Stack())) + default: + l.Print(icserror.ICSERRSTTSendFail.GetError().Error()) + } + } + derr = icserror.ICSERRSTTSendFail + }() + + // if s.sttStatus != STTMemo { + // Set EPD value + if !final { + s.uEndOfSpeech = 0 + } else { + s.uEndOfSpeech = 1 + } + + // file split + // sendCount = len(voicedata) / 320 + sendCount = len(voicedata) / 1600 + // l.Printf("SendSTT voice size: %d, sendCount: %d \n", len(voicedata), sendCount) + + //start := time.Now() + // send voice(voicedata) + for j := 0; j < sendCount; j++ { + // buff1 := voicedata[320*j : 320*(j+1)] + buff1 := voicedata[1600*j : 1600*(j+1)] + + //recording audio + if *s.RecordStart { + s.rrData.AddAudio(buff1) + } else { + s.rrData.AddTempAudio(buff1) + } + + csUDataSize := C.int(s.uDataSize) + + // if !final && j < sendCount-1 { + // s.uEndOfSpeech = 0 + // } else { + // s.uEndOfSpeech = 1 + // } + + csUEndSpeech := C.int(s.uEndOfSpeech) + csBuff := (*C.char)(unsafe.Pointer(&buff1[0])) + + // l.Printf("SelvySTT_SEND_DATA : %d, size: %d EndofSpeech : %d\n", j, csUDataSize, s.uEndOfSpeech) + + rc := C.SelvySTT_SEND_DATA(&s.STTInfo.LVCSR_SOCK_HEAD, + csBuff, + csUDataSize, + csUEndSpeech, + &s.STTInfo.LVCSR_EPD_INFO) + + if int(rc) < 0 { + l.Printf("sttselvas.go>SendSTT()> [%d] SelvySTT_SEND_DATA ERROR %d\n", int(s.sttID), rc) + ////////////////테스트코드///////////////////// + rc = C.SelvySTT_GET_ERROR(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_ERROR_RESULT) + if int(rc) < 0 { + l.Printf("sttselvas.go>SendSTT()> [%d] SelvySTT_GET_ERROR Fail.. %d\n", int(s.sttID), rc) + // return "", derr + } else { + l.Printf("sttselvas.go>SendSTT()> [%d] LVCSR_ERROR_RESULT - [%d]%s\n", int(s.sttID), s.STTInfo.LVCSR_ERROR_RESULT.nErrorCode, C.GoString(&s.STTInfo.LVCSR_ERROR_RESULT.pErrorMsg[0])) + } + /////////////////////////////////////////////// + + s.errCode, s.errMsg, s.errFunName = "54000013", "Fail", "SelvySTT_SEND_DATA" + if cbFunc != nil { + cbFunc("", 99, true) + break + } + derr = icserror.ICSERRSTTSendFail + return "", derr + } + + s.txNum++ + + if s.STTInfo.LVCSR_EPD_INFO == 1 { + //l.Printf("STT ID[%d] LVCSR_EPD_INFO: %d", int(s.sttID), s.STTInfo.LVCSR_EPD_INFO) + if s.SpeechStatus == 0 { + l.Printf("sttselvas.go>SendSTT()> STT ID[%d] LVCSR_EPD_INFO: %d\n", int(s.sttID), s.STTInfo.LVCSR_EPD_INFO) + s.SpeechStatus = 1 + + if cbFunc != nil { + cbFunc(result, int(s.STTInfo.LVCSR_EPD_INFO), true) + } + } + } else if s.STTInfo.LVCSR_EPD_INFO > 1 { + l.Printf("sttselvas.go>SendSTT()> LVCSR_EPD_INFO == %d\n", s.STTInfo.LVCSR_EPD_INFO) + s.svcReqTime = time.Now() + break + } + } + + if s.STTInfo.LVCSR_EPD_INFO == 2 || s.STTInfo.LVCSR_EPD_INFO == 5 { + // l.Printf("[%d]!!!! LVCSR_EPD_INFO: %d, LVCSR_SOCK_HEAD: %+v", int(s.sttID), s.STTInfo.LVCSR_EPD_INFO, s.STTInfo.LVCSR_SOCK_HEAD) + l.Printf("sttselvas.go>SendSTT()> STT ID[%d] LVCSR_EPD_INFO: %d, STT Completed!\n", int(s.sttID), s.STTInfo.LVCSR_EPD_INFO) + + rc := C.SelvySTT_GET_MIDRES(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_MID_RESULT) + s.svcRspTime = time.Now() + + if int(rc) < 0 { + l.Printf("sttselvas.go>SendSTT()> [%d]SelvySTT_GET_MIDRES Failed!!! - rc:%d, s.STTInfo.LVCSR_RECOG_MID_RESULT:%+v\n", int(s.sttID), rc, s.STTInfo.LVCSR_RECOG_MID_RESULT) + // s.errCode, s.errMsg, s.errFunName = "54000009", "Fail", "ASR_SVC_RECG_STR_PROC" + s.result = "$NO_RESULT$" + derr = icserror.ICSERRSTTSendFail + if cbFunc != nil { + cbFunc("", 2, true) + } + return "", icserror.ICSERRSTTContinue // 묵음으로 처리 + } + + s.sttIdx++ + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + result = C.GoString(s.STTInfo.LVCSR_RECOG_MID_RESULT.pResultStr) + resultTocken := C.GoString(s.STTInfo.LVCSR_RECOG_MID_RESULT.pDataResult.pTokenStr) + nStartTime := C.int(s.STTInfo.LVCSR_RECOG_MID_RESULT.pDataResult.nStart) + nEnd := C.int(s.STTInfo.LVCSR_RECOG_MID_RESULT.pDataResult.nEnd) + l.Printf("sttselvas.go>SendSTT()> STT ID[%d] result: %s, result: %p, pointer: %p\n", int(s.sttID), result, &result, s.STTInfo.LVCSR_RECOG_RESULT.pResultStr) + l.Printf("sttselvas.go>SendSTT()> STT ID[%d] result: %s, %d, %d\n", int(s.sttID), resultTocken, nStartTime, nEnd) + // l.Printf("STT ID[%d] result: %s, result: %p, pointer: %p\n", int(s.sttID), result, &result, s.STTInfo.LVCSR_RECOG_RESULT.pResultStr) + results := "" + if s.STTInfo.LVCSR_RECOG_MID_RESULT.nResultLen == 0 { // 길이 0일때 값 nil로 나옴 + result = "" + } + //result = "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST" + s.result = result + results = result + + if result == "$NO_RESULT$" { + results = "" + } + + resultsWithTime = fmt.Sprintf("%d|%d|%s\n", nStartTime, nEnd, results) + + if cbFunc != nil { + cbFunc(results, int(s.STTInfo.LVCSR_EPD_INFO), true) + } + //l.Printf("[%d] result Free OK", int(s.sttID)) + wg.Done() + }() + wg.Wait() + // 중간결과 구조체 메모리 해제 + rc = C.SelvySTT_CLS_MIDRES(&s.STTInfo.LVCSR_RECOG_MID_RESULT) + if int(rc) < 0 { + l.Println("sttselvas.go>SendSTT()> SelvySTT_CLS_MIDRES Failed!!! MID RES pResultStr: ", s.STTInfo.LVCSR_RECOG_MID_RESULT.pResultStr) + } + return resultsWithTime, icserror.ICSERRSTTContinue + } + // } else { + // s.uEndOfSpeech = 1 + + // //recording audio + // if *s.RecordStart { + // s.rrData.AddAudio(voicedata) + // } else { + // s.rrData.AddTempAudio(voicedata) + // } + + // csUDataSize := C.int(s.uDataSize) + // csUEndSpeech := C.int(s.uEndOfSpeech) + // csBuff := (*C.char)(unsafe.Pointer(&voicedata)) + // //csBuff := (*C.char)(unsafe.Pointer(&buff1[0])) + // rc := C.SelvySTT_SEND_DATA(&s.STTInfo.LVCSR_SOCK_HEAD, + // csBuff, + // csUDataSize, + // csUEndSpeech, + // &s.STTInfo.LVCSR_EPD_INFO) + + // if int(rc) < 0 { + // l.Printf("[%d] SelvySTT_SEND_DATA ERROR %d", int(s.sttID), rc) + // ////////////////테스트코드///////////////////// + // rc = C.SelvySTT_GET_ERROR(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_ERROR_RESULT) + // if int(rc) < 0 { + // l.Printf("[%d] SelvySTT_GET_ERROR Fail.. %d", int(s.sttID), rc) + // // return "", derr + // } else { + // l.Printf("[%d] LVCSR_ERROR_RESULT - [%d]%s", int(s.sttID), s.STTInfo.LVCSR_ERROR_RESULT.nErrorCode, C.GoString(&s.STTInfo.LVCSR_ERROR_RESULT.pErrorMsg[0])) + // } + // /////////////////////////////////////////////// + + // s.errCode, s.errMsg, s.errFunName = "54000013", "Fail", "SelvySTT_SEND_DATA" + // if cbFunc != nil { + // cbFunc("", 99, true) + // } + // derr = icserror.ICSERRSTTSendFail + // return "", derr + // } + + // s.txNum++ + + // rc = C.SelvySTT_GET_MIDRES(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_MID_RESULT) + // s.svcRspTime = time.Now() + + // if int(rc) < 0 { + // l.Printf("[%d]!!!! SelvySTT_GET_MIDRES Fail - rc:%d, s.STTInfo.LVCSR_RECOG_MID_RESULT:%+v", int(s.sttID), rc, s.STTInfo.LVCSR_RECOG_MID_RESULT) + // // s.errCode, s.errMsg, s.errFunName = "54000009", "Fail", "ASR_SVC_RECG_STR_PROC" + // s.result = "$NO_RESULT$" + // derr = icserror.ICSERRSTTSendFail + // if cbFunc != nil { + // cbFunc("", 2, true) + // } + // return "", icserror.ICSERRSTTContinue // 묵음으로 처리 + // } + + // result = C.GoString(s.STTInfo.LVCSR_RECOG_RESULT.pResultStr) + // l.Printf("STT ID[%d] result: %s, result: %p, pointer: %p", int(s.sttID), result, &result, s.STTInfo.LVCSR_RECOG_RESULT.pResultStr) + // results := "" + // if s.STTInfo.LVCSR_RECOG_RESULT.nResultLen == 0 { // 길이 0일때 값 nil로 나옴 + // result = "" + // } + // //result = "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST" + // s.result = result + // results = result + // if result == "$NO_RESULT$" { + // results = "" + // } + // if cbFunc != nil { + // cbFunc(results, int(s.STTInfo.LVCSR_EPD_INFO), true) + // } + + // //l.Printf("[%d] result Free OK", int(s.sttID)) + // return results, icserror.ICSERRSTTOK + // } + + return "", icserror.ICSERRSTTContinue +} + +// result return +func NewSTTSResult(result string, err *icserror.IcsError) *STTSResult { + return &STTSResult{result, err} +} + +// 말을 하지 않았을때 결과 조회 +func (s *STTSelvas) GetSTTResultToStopSTT() (string, *icserror.IcsError) { + l := icslog.GetIcsLog() + + if s == nil { + return "", icserror.ICSERRSTTNotInit + } + + //rc := C.SelvySTT_GET_MIDRES(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_MID_RESULT) + rc := C.SelvySTT_GET_RES(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_RESULT) + + l.Printf("GetSTTResultToStopSTT rc:%d, [%d] EPDINFO[%d] LVCSR_RECOG_RESULT: %+v, LVCSR_RECOG_RESULT:%+v\n", int(rc), int(s.sttID), s.STTInfo.LVCSR_EPD_INFO, &s.STTInfo.LVCSR_RECOG_RESULT, s.STTInfo.LVCSR_RECOG_RESULT) + + if int(rc) < 0 { + l.Printf("[%d] SelvySTT_GET_MIDRES Fail - rc:%d, s.STTInfo.LVCSR_RECOG_MID_RESULT:%+v\n", int(s.sttID), rc, s.STTInfo.LVCSR_RECOG_MID_RESULT) + s.errCode, s.errMsg, s.errFunName = "54000009", "Fail", "SelvySTT_GET_MIDRES" + // s.SendStasticInfo(s.callID, s.custID, s.trid, "STT", "ASR_SVC_RECG_STR_PROC", "54000001", "Fail", "", "", s.reqTime, time.Now(), s.sreserved, svcReqTime, time.Now()) + return "", icserror.ICSERRSTTSendFail + } + return "", icserror.ICSERRSTTContinue +} + +// epd 1 이후 end 전송 => 결과조회 +func (s *STTSelvas) SendSTTProcGetResult() (string, *icserror.IcsError) { + var derr *icserror.IcsError = nil + if s == nil { + return "", icserror.ICSERRSTTNotInit + } + + //buff1 := make([]byte, 1) + s.uEndOfSpeech = 1 + uDataSize := 0 + csUDataSize := C.int(uDataSize) + csUEndSpeech := C.int(s.uEndOfSpeech) + //csBuff := (*C.char)(unsafe.Pointer(&buff1[0])) + // csBuff := nil + // svcReqTime := time.Now() + rc := C.SelvySTT_SEND_DATA(&s.STTInfo.LVCSR_SOCK_HEAD, + nil, + csUDataSize, + csUEndSpeech, + &s.STTInfo.LVCSR_EPD_INFO) + + if int(rc) < 0 { + l.Printf("[ERR] sttselvas>SendSTTProcGetResult()> [%d] SelvySTT_SEND_DATA Failed - rc:%d, s.STTInfo.LVCSR_RECOG_RESULT:%+v\n", int(s.sttID), rc, s.STTInfo.LVCSR_RECOG_RESULT) + derr = icserror.ICSERRSTTSendFail + s.errCode, s.errMsg, s.errFunName = "54000009", "Fail", "SelvySTT_SEND_DATA" + // s.SendStasticInfo(s.callID, s.custID, s.trid, "STT", "SelvySTT_SEND_DATA", "54000001", "Fail", "", "", s.reqTime, time.Now(), s.sreserved, svcReqTime, time.Now()) + return "", derr + } + + l.Printf("[LOG] sttselvas>SendSTTProcGetResult()> rc:%d, [%d] EPDINFO[%d] LVCSR_RECOG_RESULT: %+v, LVCSR_RECOG_RESULT:%+v\n", + int(rc), int(s.sttID), s.STTInfo.LVCSR_EPD_INFO, &s.STTInfo.LVCSR_RECOG_RESULT, s.STTInfo.LVCSR_RECOG_RESULT) + + if s.STTInfo.LVCSR_EPD_INFO == 2 { + // l.Printf("[%d]!!!! LVCSR_EPD_INFO: %d, LVCSR_SOCK_HEAD: %+v", int(s.sttID), s.STTInfo.LVCSR_EPD_INFO, s.STTInfo.LVCSR_SOCK_HEAD) + l.Printf("[LOG] sttselvas>SendSTTProcGetResult()> STT ID[%d] LVCSR_EPD_INFO: %d, STT Completed!\n", int(s.sttID), s.STTInfo.LVCSR_EPD_INFO) + rc := C.SelvySTT_GET_MIDRES(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_MID_RESULT) + s.svcRspTime = time.Now() + + if int(rc) < 0 { + l.Printf("[ERR] sttselvas>SendSTTProcGetResult()> [%d] SelvySTT_GET_MIDRES Failed - rc:%d, s.STTInfo.LVCSR_RECOG_MID_RESULT:%+v\n", int(s.sttID), rc, s.STTInfo.LVCSR_RECOG_MID_RESULT) + // s.errCode, s.errMsg, s.errFunName = "54000009", "Fail", "ASR_SVC_RECG_STR_PROC" + s.result = "$NO_RESULT$" + // derr = icserror.ICSERRSTTSendFail + + return "", icserror.ICSERRSTTContinue // 묵음으로 처리 + } + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + result := C.GoString(s.STTInfo.LVCSR_RECOG_MID_RESULT.pResultStr) + l.Printf("[LOG] sttselvas>SendSTTProcGetResult()> STT ID[%d] result: %s, result: %p, pointer: %p\n", int(s.sttID), result, &result, s.STTInfo.LVCSR_RECOG_RESULT.pResultStr) + results := "" + if s.STTInfo.LVCSR_RECOG_MID_RESULT.nResultLen == 0 { // 길이 0일때 값 nil로 나옴 + result = "" + } + //result = "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST" + s.result = result + results = result + if result == "$NO_RESULT$" { + results = "" + } + + l.Printf("[LOG] sttselvas>SendSTTProcGetResult()> results: %s", results) + //l.Printf("[%d] result Free OK", int(s.sttID)) + wg.Done() + }() + wg.Wait() + } + + // 중간결과 구조체 메모리 해제 + rc = C.SelvySTT_CLS_MIDRES(&s.STTInfo.LVCSR_RECOG_MID_RESULT) + if int(rc) < 0 { + l.Println("[ERR] sttselvas>SendSTTProcGetResult()> SelvySTT_CLS_MIDRES Failed. LVCSR_RECOG_MID_RESULT.pResultStr: ", s.STTInfo.LVCSR_RECOG_MID_RESULT.pResultStr) + } + + // 최종 결과 호출 + rc = C.SelvySTT_GET_RES(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_RESULT) + + if int(rc) < 0 { + l.Printf("[ERR] sttselvas>SendSTTProcGetResult()> [%d] SelvySTT_GET_RES Fail - rc:%d, s.STTInfo.SelvySTT_GET_RES:%+v\n", int(s.sttID), rc, s.STTInfo.LVCSR_RECOG_RESULT) + derr = icserror.ICSERRSTTSendFail + s.errCode, s.errMsg, s.errFunName = "54000009", "Fail", "SelvySTT_GET_RES" + return "", derr + } + + dataCnt := int(s.STTInfo.LVCSR_RECOG_RESULT.nDataCnt) + resultLen := int(s.STTInfo.LVCSR_RECOG_RESULT.nResultLen) + pResultStr := C.GoString(s.STTInfo.LVCSR_RECOG_RESULT.pResultStr) + + l.Printf("dataCnt: %d", dataCnt) + l.Printf("resultLen: %d", resultLen) + + if pResultStr != "" { + strresult := C.getResultData(s.STTInfo.LVCSR_RECOG_RESULT.pDataResult, C.int(dataCnt)) + l.Printf("[LOG] sttselvas>SendSTTProcGetResult()> SelvySTT_GET_RES 인식 결과: %s", C.GoString(strresult)) + defer C.free(unsafe.Pointer(strresult)) + + if resultLen > 0 { + l.Printf("[LOG] sttselvas>SendSTTProcGetResult()> SelvySTT_GET_RES 인식 결과: 연속어 [ %s ], 첫음 [ %d ] 끝음 [ %d ] 스코어 [ %f ]", + pResultStr, s.STTInfo.LVCSR_RECOG_RESULT.pDataResult.nStart, s.STTInfo.LVCSR_RECOG_RESULT.pDataResult.nEnd, s.STTInfo.LVCSR_RECOG_RESULT.fConfidScore) + } else { + return "", icserror.ICSERRSTTFail + } + // if dataCnt > 0 { + // for i := 0; i < dataCnt; i++ { + // l.Printf(icslog.LOG_LEVEL_INFO, -1, "음성인식 결과 : 인식어 [ %s ], 시작시간 [ %d ] 종료시간 [ %d ]", s.STTInfo.LVCSR_RECOG_RESULT.pDataResult[i].pTokenStr, s.STTInfo.LVCSR_RECOG_RESULT.pDataResult[i].nStart, s.STTInfo.LVCSR_RECOG_RESULT.pDataResult[i].nEnd) + // } + + // } + + //s.GetSTTResultToStopSTT() + // fmt.Print("Request Recg Result") + // rc = C.ASR_SVC_RECG_PROC(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_RESULT) + // if int(rc) < 0 { + // derr = icserror.ICSERRSTTFail + // return "", derr + // } + returnstr := C.GoString(strresult) + return returnstr, nil + } + return "", nil +} + +func long(dataCnt int) { + panic("unimplemented") +} + +// func getResultMessage(result *C.LVCSR_RECOG_RESULT, size int) []string { +// messages := make([]string, size) + +// for i := 0; i < size; i++ { +// cMsg := C.GoString(result.pDataResult) +// } +// return s.rrData.GetAudio() +// } + +func (s STTSelvas) GetAudio() []byte { + return s.rrData.GetAudio() +} + +func (s STTSelvas) GetReqTime() time.Time { + return s.reqTime +} diff --git a/icsutil/util.go b/icsutil/util.go new file mode 100644 index 0000000..fa01e45 --- /dev/null +++ b/icsutil/util.go @@ -0,0 +1,125 @@ +package icsutil + +import ( + "batchmodule/icsconf" + "batchmodule/icserror" + "batchmodule/icslog" + "encoding/binary" + "fmt" + "io" + "log" + "os" +) + +var ( + conf icsconf.AppInfo + l *log.Logger +) + +func init() { + conf = icsconf.Getconfig() + l = icslog.InitializeLogger() +} + +func DeletePcmFolder(dir string) (bool, *icserror.IcsError) { + l.Println("util.go>deletePcmFolder> dir: ", dir) + if _, err := os.Stat(dir); os.IsNotExist(err) { + l.Println("Error at util.go>deletePcmFolder> pcm folder does not exist with the dir: ", dir) + return false, icserror.ICSERRFileOpen + } + err := os.RemoveAll(dir) + if err != nil { + l.Panicln("Error at util.go>deletePcmFolder> os.RemoveAll() dir: ", dir) + return false, icserror.ICSERRWriteFile + } + l.Printf(`util.go>deletePcmFolder> %s has been successfully deleted`, dir) + return true, nil +} + +func DevideWavTo2Pcm(fileName string, starttime string) (result bool, folderName string) { + pcmDir := conf.Directories.PCMDirectory + + starttimeDir := fmt.Sprintf("%s/%s", pcmDir, starttime) + os.MkdirAll(starttimeDir, os.ModePerm) + + leftOutputFile := fmt.Sprintf(`%s/%s-left.pcm`, starttimeDir, starttime) + rightOutputFile := fmt.Sprintf(`%s/%s-right.pcm`, starttimeDir, starttime) + + inFile, err := os.Open(fileName) + if err != nil { + l.Printf("Error at util.go>devideWavTo2Pcm()> os.Open failed. dir: %s", fileName) + return false, "" + } + defer inFile.Close() + + leftFile, err := os.Create(leftOutputFile) + if err != nil { + l.Printf("Error at util.go>devideWavTo2Pcm()> os.Create failed. dir: %s", leftOutputFile) + return false, "" + } + defer leftFile.Close() + + rightFile, err := os.Create(rightOutputFile) + if err != nil { + l.Printf("Error at util.go>devideWavTo2Pcm()> os.Create failed. dir: %s", leftOutputFile) + return false, "" + } + defer rightFile.Close() + + // Skip WAV header (44 bytes) + wavHeader := make([]byte, 44) + if _, err := inFile.Read(wavHeader); err != nil { + l.Printf("Error at util.go>devideWavTo2Pcm()> inFile.Read(wavHeader) failed. err: %+v", err) + return false, "" + } + + // Check if it's a valid WAV file + if string(wavHeader[:4]) != "RIFF" || string(wavHeader[8:12]) != "WAVE" { + l.Printf("Error at util.go>devideWavTo2Pcm()> invalid WAV file format. Header content: %s", string(wavHeader[:4])) + return false, "" + } + + // Check if WAV file is stereo + numChannels := binary.LittleEndian.Uint16(wavHeader[22:24]) + if numChannels != 2 { + l.Printf("Error at util.go>devideWavTo2Pcm()> unsupported channel count: %d. This function only supports stereo (2 channels)", numChannels) + return false, "" + } + + buf := make([]byte, 4) // 2 bytes per sample * 2 channels + + for { + n, err := inFile.Read(buf) + if err == io.EOF { + break + } + if err != nil { + l.Printf("Error at util.go>devideWavTo2Pcm()> inFile.Read(buf) failed. failed to read from input file: %v", err) + return false, "" + } + + if n < 4 { + for i := n; i < 4; i++ { + buf[i] = 0 + } + break + } + + leftSample := buf[:2] + rightSample := buf[2:] + + if err := binary.Write(leftFile, binary.LittleEndian, leftSample); err != nil { + l.Printf("Error at util.go>devideWavTo2Pcm()> binary.Write(leftFile, binary.LittleEndian, leftSample) failed. err: %v", err) + return false, "" + } + + if err := binary.Write(rightFile, binary.LittleEndian, rightSample); err != nil { + l.Printf("Error at util.go>devideWavTo2Pcm()> binary.Write(rightFile, binary.LittleEndian, rightSample) failed. err: %v", err) + return false, "" + } + + } + + l.Printf("util.go>devideWavTo2Pcm()> WAV file split successfully to dir: %s, %s", leftOutputFile, rightOutputFile) + return true, starttime +} diff --git a/main.go b/main.go index 268c719..5a01f5d 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,14 @@ package main import ( - mybatch "mybatch/icsbatch" - // stt "mybatch/icsstt" + batchmodule "batchmodule/icsbatch" + // stt "batchmodule/icsstt" ) func main() { - mybatch.BatchForPostRequestForDataList() - // mybatch.BatchForPostRequestForDataListTest() + batchmodule.BatchForFailedSTT() + // batchmodule.BatchForPostRequestForDataListTest() // 배치 없이 http, ws, stt 바로 호출 //stt.ProcessPostRequestForDataList()