Browse Source

test update

dujunchen 3 weeks ago
parent
commit
3d3f35535d

+ 3 - 0
.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+    "git.ignoreLimitWarning": true
+}

+ 12 - 33
configs/config.demo.yaml

@@ -1,45 +1,24 @@
-identityKey: 'zycoozycoo' # jwt secret
-
 # AMI
 asteriskAMIHost: '127.0.0.1'
 asteriskAMIPort: '5038'
 asteriskAMIUser: 'admin'
 asteriskAMISecret: 'admin'
 
-# AGI
-asteriskAGIPort: '18080'
-
-asteriskBroadcastTimeout: 30
-asteriskBroadcastName: Broadcast
-asteriskBroadcastID: Broadcast
-
-# Trigger
-asteriskTriggerPath: '/etc/asterisk/extensions_trigger.conf'
-# Paging
-asteriskPagingPath : '/etc/asterisk/extensions_paging.conf'
-
 # 数据库配置
-mysqlDBHost: '127.0.0.1'
-mysqlDBUser: 'coovox_admin'
-mysqlDBName: 'ippbx_db'
-mysqlDBSecret: 'ZycooCoovoxDba42'
+#mysqlDBHost: '127.0.0.1'
+#mysqlDBUser: 'debian-sys-maint'
+#mysqlDBName: 'PA'
+#mysqlDBSecret: '4Ae8GlfBN9Nq59Xd'
 
 # Redis
-redisDBHost: 'localhost'
-redisDBPort: '6379'
-redisDBSecret: ''
+#redisDBHost: 'localhost'
+#redisDBPort: '6379'
+#redisDBSecret: ''
 
 # log
-logErrorPath: './error.log'
-logInfoPath: './info.log'
-
-# music upload
-storagePath: '/tmp'
-
-webhost: 0.0.0.0
-webport: 8080
+logErrorPath: '/data/test/log/error.log'
+logInfoPath: '/data/test/log/info.log'
+# log
 
-#cors
-allowOrigin: http://localhost:8080
-#push
-allEventPushUrl:
+processRecord: 'yes'
+recordingEvent: '/data/test/log/recordEvent.log'

+ 12 - 23
configs/config.yaml

@@ -1,35 +1,24 @@
-identityKey: 'zycoozycoo' # jwt secret
-
 # AMI
 asteriskAMIHost: '127.0.0.1'
 asteriskAMIPort: '5038'
 asteriskAMIUser: 'admin'
 asteriskAMISecret: 'admin'
 
-# AGI
-asteriskAGIPort: '18080'
-
-asteriskBroadcastTimeout: 30
-asteriskBroadcastName: Broadcast
-asteriskBroadcastID: Broadcast
-
-# Trigger
-asteriskTriggerPath: '/etc/asterisk/extensions_trigger.conf'
-# Paging
-asteriskPagingPath : '/etc/asterisk/extensions_paging.conf'
-
 # 数据库配置
-mysqlDBHost: '127.0.0.1'
-mysqlDBUser: 'ippbx'
-mysqlDBName: 'ippbx_db'
-mysqlDBSecret: '123456'
+#mysqlDBHost: '127.0.0.1'
+#mysqlDBUser: 'debian-sys-maint'
+#mysqlDBName: 'PA'
+#mysqlDBSecret: '4Ae8GlfBN9Nq59Xd'
 
 # Redis
-redisDBHost: 'localhost'
-redisDBPort: '6379'
-redisDBSecret: ''
+#redisDBHost: 'localhost'
+#redisDBPort: '6379'
+#redisDBSecret: ''
 
 # log
-logErrorPath: './error.log'
-logInfoPath: './info.log'
+logErrorPath: '/data/test/log/error.log'
+logInfoPath: '/data/test/log/info.log'
+# log
 
+processRecord: 'yes'
+recordingEvent: '/data/test/log/recordEvent.log'

+ 1 - 0
internal/app/ami/action/call.go

@@ -98,6 +98,7 @@ func HangupAllExcept(caller string) {
 
 // Hangup all ICP
 func HangupRunningTask(toRunTask string) {
+	return
 	lfshook.NewLogger().Infof("===HangupRunningTask=toRuntask=%s====   RunningTask:%s===", toRunTask, priority.RunningType)
 
 	//same type return

+ 74 - 19
internal/app/ami/action/index.go

@@ -20,6 +20,7 @@ import (
 )
 
 var AminInstance *amigo.Amigo
+var trainInfo = ""
 
 func HandleAMI(event map[string]string) {
 	//lfshook.NewLogger().Infof("===start======%s", event["Event"]
@@ -56,13 +57,18 @@ func HandleAMI(event map[string]string) {
 	case "UserEvent": // RCD filename; PA;CPA; CabCab
 		lfshook.NewLogger().Infof("========event:%s   File:%s", event["Event"], event["FILENAME"])
 
-		if event["UserEvent"] == "SetRecordFile" { //Get  record file name
-			//延时一下1秒;
-			//time.Sleep(time.Second)
+		//Get  record file name ,encode and upload
+		if event["UserEvent"] == "SetRecordFile" {
+
+			if configs.ConfigGlobal.ProcessRecord != "yes" {
+				break
+			}
 
 			//检测录音文件是否存在;最多检测5次,每次间隔1秒
 			var fileExists bool
 			for i := 0; i < 5; i++ {
+				time.Sleep(time.Second) // 等待1秒
+
 				if _, err := os.Stat(event["FILENAME"]); err == nil {
 					fileExists = true
 					lfshook.NewLogger().Infof("File found: %s", event["FILENAME"])
@@ -70,34 +76,83 @@ func HandleAMI(event map[string]string) {
 				} else if os.IsNotExist(err) {
 					lfshook.NewLogger().Infof("File not found (attempt %d): %s", i+1, event["FILENAME"])
 				} else {
-					lfshook.NewLogger().Errorf("Error checking file: %v", err)
+					lfshook.NewLogger().Infof("Error checking file: %v", err)
 				}
-				time.Sleep(time.Second) // 等待1秒
 			}
 
-			if !fileExists {
-				lfshook.NewLogger().Errorf("File not found after 5 attempts: %s", event["FILENAME"])
-				// 可选:发送错误通知或跳过后续处理
+			if !fileExists { //5秒内没有生成录音文件
+				lfshook.NewLogger().Infof("File %s not found after 5 attempts", event["FILENAME"])
 				break
 			}
 
-			//检测录音文件是否超过3min;
+			//获取录音文件时长,检测录音文件是否超过3min;
 			duration, err := utils.GetDuration(event["FILENAME"])
 			if err != nil {
-				lfshook.NewLogger().Errorf("Get duration err: %+v", err)
+				utils.Logger.Printf("Get duration err: %+v", err)
+				break
 			}
-
-			//如果超过则切割
-			if duration > 180 {
-
+			lfshook.NewLogger().Infof("==========duration===== %s", duration)
+			//转wav文件的采样率到22kHz,并切割位180秒每段
+			var FileNames []string
+			if duration >= 600 { //超过600秒的超长文件,不处理
+				lfshook.NewLogger().Infof("File over 600 sec, Ignored !")
+				break
+			} else if duration < 600 { //小于600秒文件进行转换和切割
+				FileNames, err = utils.ConvertAndSegmentWAV(event["FILENAME"], strings.Replace(event["FILENAME"], ".wav", "", -1))
+				if err != nil {
+					lfshook.NewLogger().Infof("Get duration err: %+v", err)
+					break
+				}
+				lfshook.NewLogger().Infof("=============== File %+v found after convert", FileNames)
 			}
 
-			//执行加密操作
-			DstFile := fmt.Sprintf("%s.bin", event["FILENAME"])
-			utils.AudioFileEncode(DstFile, event["FILENAME"])
+			//执行加密操作,并将录音信息写入日志文件
+			DstFile := ""
+			if len(FileNames) > 0 { // 文件切割之后进入循环处理
+				for _, file := range FileNames {
+					DstFile = fmt.Sprintf("%s.bin", file)
 
-			//切割&加密之后推送生成的文件名;
-			alstatus.SendRecordFile(DstFile, event["RecordType"])
+					lfshook.NewLogger().Infof("===========Bin file====%s", DstFile)
+					err = utils.AudioFileEncode(DstFile, file)
+					if err != nil {
+						lfshook.NewLogger().Infof("Encode file: %s err: %+v", DstFile, err)
+						continue
+					}
+					//切割&加密之后发送生成的文件名到STC;
+					alstatus.SendRecordFile(DstFile, event["RecordType"])
+
+					trainInfo = active.ActivedCab
+
+					if strings.Contains(event["FILENAME"], "PAD") {
+						_, caller, callee := utils.GetPadInfo(event["FILENAME"])
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: PAD , CabNumber: %c , LocationCode: %c, Connected: %s, RecordFileName:%s", trainInfo, caller[2], caller[3], callee, DstFile)
+					} else if strings.Contains(event["FILENAME"], "PA") {
+						_, caller, _ := utils.GetPadInfo(event["FILENAME"])
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: PA, Caller: %s, RecordFileName: %s", trainInfo, caller, DstFile)
+					} else if strings.Contains(event["FILENAME"], "C2C") {
+						_, caller, _ := utils.GetPadInfo(event["FILENAME"])
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: CabCab, Caller: %s, RecordFileName: %s", trainInfo, caller, DstFile)
+					} else if strings.Contains(event["FILENAME"], "CPA") {
+						_, caller, _ := utils.GetPadInfo(event["FILENAME"])
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: CPA, Caller: %s, RecordFileName: %s", trainInfo, caller, DstFile)
+					} else if strings.Contains(event["FILENAME"], "EMG") {
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: EMG, RecordFileName: %s", trainInfo, DstFile)
+					} else if strings.Contains(event["FILENAME"], "STN") {
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: STN, RecordFileName: %s", trainInfo, DstFile)
+					} else if strings.Contains(event["FILENAME"], "DCS") {
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: DCS, RecordFileName: %s", trainInfo, DstFile)
+					} else if strings.Contains(event["FILENAME"], "SPC") {
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: SPC, RecordFileName: %s", trainInfo, DstFile)
+					} else if strings.Contains(event["FILENAME"], "CHK") {
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: Self Check, RecordFileName: %s", trainInfo, DstFile)
+					} else if strings.Contains(event["FILENAME"], "TONE") {
+						utils.Logger.Printf("Train Information: CabNumber %s, MessageType: TONE Test, RecordFileName: %s", trainInfo, DstFile)
+					}
+				}
+			} else {
+				lfshook.NewLogger().Infof("No files to upload!!!")
+				break
+			}
 
 		} else if event["UserEvent"] == "CallType" && (event["Type"] == "PA" || event["Type"] == "CPA") { //PA start; check manual PA priority
 			//PA & CPA interrupt others

+ 2 - 0
internal/app/stc/broadcast/stc-broadcast.go

@@ -474,6 +474,8 @@ func AlarmHandleICP(data []byte) {
 		} else if active.ActivedCab == "8" {
 			action.Dial("0402", "0511", "pad-rule-pacus", "ani8", exten, "8") // PACUs dial ICP8
 			//goto ami event ConfbridgeJoin, ICP answer PAD
+		} else if active.ActivedCab == "" { // No cab occupied
+			action.Dial("0402", "0511", "pad-rule-pacus", "ani1", exten, "1") // PACUs dial ICP1
 		}
 
 	case 0x02: //hold  重新放回队列里面

+ 17 - 3
internal/app/stc/index.go

@@ -9,6 +9,7 @@ import (
 	msgdata "pbx-api-gin/internal/app/stc/data"
 	"pbx-api-gin/internal/app/stc/socket"
 	"pbx-api-gin/pkg/lfshook"
+	"pbx-api-gin/pkg/utils"
 	"sync"
 	"syscall"
 
@@ -20,16 +21,23 @@ func StartStcConnection(conn net.Conn, cab string) {
 	var connMux sync.Mutex // 保护 conn 的读写
 	var conn1 net.Conn
 	var err error
+	var logTag = 0
 
 	for {
 		// 尝试建立连接MC
 		conn1, err = CreateConnection(cab)
 		if err != nil || conn1 == nil {
 			time.Sleep(2 * time.Second)
-			lfshook.NewLogger().Logger.Infof("===========Reconnecting====Cab:%s=======", cab)
+			lfshook.NewLogger().Logger.Infof("===========Reconnecting====To Cab:%s=======", cab)
 			continue
 		}
 
+		//set connection log
+		if logTag == 0 {
+			utils.Logger.Printf("Train Information: CabNumber %s, Message: Connection to Cab%s STC is up !", active.ActivedCab, cab)
+			logTag = 1
+		}
+
 		connMux.Lock()
 		oldConn := conn
 		if cab == "1" {
@@ -42,7 +50,7 @@ func StartStcConnection(conn net.Conn, cab string) {
 		// 关闭旧连接(如果存在)
 		if oldConn != nil {
 			oldConn.Close()
-			lfshook.NewLogger().Logger.Infof("Closed previous connection")
+			//lfshook.NewLogger().Logger.Infof("Closed previous connection")
 		}
 
 		// 使用 context 控制所有协程的生命周期
@@ -71,7 +79,13 @@ func StartStcConnection(conn net.Conn, cab string) {
 		cancel() // 确保所有 cancel 被调用
 		conn1.Close()
 
-		lfshook.NewLogger().Logger.Info("Reconnecting in 1 second...")
+		//set connection log
+		if logTag == 1 {
+			utils.Logger.Printf("Train Information: CabNumber %s, Message: Connection to Cab%s STC is down !", active.ActivedCab, cab)
+			logTag = 0
+		}
+
+		//lfshook.NewLogger().Logger.Info("Reconnecting in 1 second...")
 		time.Sleep(time.Second) // 重连前等待
 	}
 }

+ 12 - 9
internal/pkg/configs/decode.go

@@ -13,14 +13,14 @@ import (
 // Config https://godoc.org/gopkg.in/yaml.v2
 // Config 存储配置
 type Config struct {
-	IdentityKey string `yaml:"identityKey"`
+	//IdentityKey string `yaml:"identityKey"`
 
 	AsteriskAMIHost   string `yaml:"asteriskAMIHost"`   // Host
 	AsteriskAMIPort   string `yaml:"asteriskAMIPort"`   // Port
 	AsteriskAMIUser   string `yaml:"asteriskAMIUser"`   // User
 	AsteriskAMISecret string `yaml:"asteriskAMISecret"` // Secret
 
-	AsteriskAGIPort string `yaml:"asteriskAGIPort"` // AGIPort
+	//AsteriskAGIPort string `yaml:"asteriskAGIPort"` // AGIPort
 
 	//AsteriskTriggerPath      string `yaml:"asteriskTriggerPath"`      // 广播触发规则
 	//AsteriskPagingPath       string `yaml:"asteriskPagingPath"`       // 广播规则
@@ -28,17 +28,19 @@ type Config struct {
 	//AsteriskBroadcastName    string `yaml:"asteriskBroadcastName"`    // 广播时名称
 	//AsteriskBroadcastID      string `yaml:"asteriskBroadcastID"`      // 广播时ID
 
-	MysqlDBHost   string `yaml:"mysqlDBHost"`   // Host
-	MysqlDBUser   string `yaml:"mysqlDBUser"`   // User
-	MysqlDBSecret string `yaml:"mysqlDBSecret"` // Secret
-	MysqlDBName   string `yaml:"mysqlDBName"`   // Name
+	//MysqlDBHost   string `yaml:"mysqlDBHost"`   // Host
+	//MysqlDBUser   string `yaml:"mysqlDBUser"`   // User
+	//MysqlDBSecret string `yaml:"mysqlDBSecret"` // Secret
+	//MysqlDBName   string `yaml:"mysqlDBName"`   // Name
 
 	//RedisDBHost   string `yaml:"redisDBHost"`   // Host
 	//RedisDBPort   string `yaml:"redisDBPort"`   // User
 	//RedisDBSecret string `yaml:"redisDBSecret"` // Secret
 
-	LogInfoPath  string `yaml:"logInfoPath"`  //logInfoPath
-	LogErrorPath string `yaml:"logErrorPath"` //logErrorPath
+	LogInfoPath    string `yaml:"logInfoPath"`    //logInfoPath
+	LogErrorPath   string `yaml:"logErrorPath"`   //logErrorPath
+	RecordEventLog string `yaml:"recordingEvent"` //recordingEventfile
+	ProcessRecord  string `yaml:"processRecord"`  //processRecord
 
 	//StoragePath string `yaml:"storagePath"` // 存储目录
 
@@ -47,7 +49,8 @@ type Config struct {
 	//WebHost string `yaml:"webhost"` //Host
 	//WebPort int64  `yaml:"webport"` //port
 
-	AllowOrigin string `yaml:"allowOrigin"` // allowOrigin
+	//AllowOrigin string `yaml:"allowOrigin"` // allowOrigin
+	//AllowOrigin string `yaml:"allowOrigin"` // allowOrigin
 
 	LogLevel log.Level //logLevel
 }

+ 92 - 28
pkg/utils/file.go

@@ -2,19 +2,18 @@ package utils
 
 import (
 	"encoding/base64"
+	"fmt"
 	"io"
+	"log"
 	"os"
 	"os/exec"
+	"path/filepath"
+	"pbx-api-gin/internal/pkg/configs"
+	"pbx-api-gin/pkg/lfshook"
 	"strconv"
 	"strings"
-
-	"github.com/sirupsen/logrus"
-	"gopkg.in/ini.v1"
 )
 
-// 缓存 timeZone 信息
-var timeZoneCache string
-
 // FileExists checks if a file exists and is not a directory before we
 // try using it to prevent further errors.
 func FileExists(filename string) bool {
@@ -25,39 +24,26 @@ func FileExists(filename string) bool {
 	return !info.IsDir()
 }
 
-// pbx 不能正确读取时区, 读取自定义文件  /etc/asterisk/ntp.conf
-func GetLocationName() string {
-	if timeZoneCache != "" {
-		return timeZoneCache
-	}
-	filePath := "/etc/asterisk/ntp.conf"
-	_, err := os.Stat(filePath)
-	if err != nil {
-		logrus.Error(err)
-		return ""
-	}
-	iniFile, err := ini.Load(filePath)
-	if err != nil {
-		logrus.Error(err)
-		return ""
-	}
-	timeZoneCache = iniFile.Section("ntp").Key("TZNAME").Value()
-	logrus.Infof("use location %s", timeZoneCache)
-	return timeZoneCache
-}
-
 func GetDuration(filePath string) (int, error) {
-	cmd := exec.Command("soxi", "-D", filePath)
+	cmd := exec.Command("ffprobe",
+		"-v", "quiet",
+		"-show_entries", "format=duration",
+		"-of", "default=nw=1:nokey=1",
+		filePath,
+	)
+
 	output, err := cmd.Output()
 	if err != nil {
 		return 0, err
 	}
 
+	// 解析输出(纯数字,单位秒)
 	durationStr := strings.TrimSpace(string(output))
 	duration, err := strconv.ParseFloat(durationStr, 64)
 	if err != nil {
 		return 0, err
 	}
+
 	return int(duration), nil
 }
 
@@ -96,3 +82,81 @@ func AudioFileEncode(dstFile, srcFile string) error {
 	}
 	return err
 }
+
+// ConvertAndSegmentWAV 将 WAV 文件转为 22kHz 并每 180 秒切分,返回生成的文件名列表
+func ConvertAndSegmentWAV(inputFile string, outputPrefix string) ([]string, error) {
+	// 输出文件命名模式,如:output_%03d.wav
+	outputPattern := outputPrefix + "_%03d.wav"
+
+	// 构建 ffmpeg 命令
+	cmd := exec.Command("ffmpeg",
+		"-i", inputFile, // 输入文件
+		"-ar", "22050", // 设置采样率为 22050 Hz (22kHz)
+		"-f", "segment", // 启用分段模式
+		"-segment_time", "180", // 每段 180 秒
+		"-c:a", "pcm_s16le", // 音频编码格式(标准 WAV)
+		"-reset_timestamps", "1", // 重置时间戳,每个片段从 0 开始
+		outputPattern, // 输出文件命名规则
+	)
+
+	// 执行命令并捕获输出
+	output, err := cmd.CombinedOutput()
+	if err != nil && !strings.Contains(string(output), "Conversion failed!") {
+		// 即使有警告(如非关键错误),只要文件生成了就继续解析
+		log.Printf("ffmpeg 执行时有警告: %v", err)
+	}
+	// 解析输出,提取所有生成的文件名
+	files := extractFilenamesFromFFmpegOutput(outputPrefix)
+
+	lfshook.NewLogger().Infof("=============== found after convert outfiles %+v ", files)
+	return files, nil
+}
+
+// extractFilenamesFromFFmpegOutput 从 ffmpeg 输出日志中提取生成的文件名
+func extractFilenamesFromFFmpegOutput(prefix string) []string {
+	pattern := fmt.Sprintf("%s_*", prefix)
+	matches, err := filepath.Glob(pattern)
+	if err != nil {
+		log.Fatal("模式匹配错误:", err)
+	}
+
+	return matches
+}
+
+func WriteLogToFile(message string) {
+	// 打开文件,支持追加模式,如果文件不存在则创建
+	file, err := os.OpenFile(configs.ConfigGlobal.RecordEventLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
+	if err != nil {
+		return
+	}
+	defer file.Close()
+
+	// 创建一个写入到文件的日志器
+	logger := log.New(file, "", log.LstdFlags)
+
+	// 写入日志消息
+	logger.Println(message)
+}
+
+var Logger *log.Logger
+
+func init() {
+	file, _ := os.OpenFile("/data/test/log/recordEvent.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
+	Logger = log.New(file, "", log.LstdFlags) // 自动带时间戳
+}
+
+func GetPadInfo(base string) (padType, padNum, connectedCab string) {
+	// 按 '/' 分割字符串
+	parts := strings.Split(base, "/")
+
+	// 取最后一段
+	lastPart := parts[len(parts)-1]
+
+	// 最后一段中,按‘-’分割返回前三段:parts[0], parts[1], parts[2] → PAD-2411-1411
+	ret := strings.Split(lastPart, "-")
+	if len(ret) < 3 {
+		return
+	}
+
+	return ret[1], ret[2], ret[3]
+}