1
0

5 Revīzijas 40b37513d1 ... 55227dc75d

Autors SHA1 Ziņojums Datums
  dujunchen 55227dc75d audio file encrypt , CPA update 1 mēnesi atpakaļ
  dujunchen 3d3f35535d test update 1 mēnesi atpakaļ
  dujunchen 0314518248 update 1 mēnesi atpakaļ
  dujunchen 382f22dad4 update 1 mēnesi atpakaļ
  dujunchen e8c10cbff9 update rcd 1 mēnesi atpakaļ

+ 3 - 0
.vscode/settings.json

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

+ 1 - 3
cmd/main.go

@@ -6,7 +6,6 @@ import (
 	"pbx-api-gin/pkg/lfshook"
 	"pbx-api-gin/pkg/utils"
 
-	"github.com/gin-gonic/gin"
 	"github.com/sirupsen/logrus"
 	"gopkg.in/natefinch/lumberjack.v2"
 )
@@ -26,8 +25,7 @@ func main() {
 
 	configs.ConfigGlobal.LogLevel = logrus.InfoLevel
 
-	gin.SetMode(gin.ReleaseMode)
-
+	//gin.SetMode(gin.ReleaseMode)
 	//gin.SetMode(gin.DebugMode)
 
 	lfshook.NewLogger().Logger.SetReportCaller(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'

+ 7 - 3
internal/app/ami/action/call.go

@@ -26,6 +26,8 @@ func InActiveHangup() {
 			HangupRunningTask("InActiveHangup")
 		case "VOL":
 			HangupRunningTask("InActiveHangup")
+		case "PAD-OCC":
+			HangupRunningTask("InActiveHangup")
 		}
 	}
 }
@@ -96,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
@@ -139,10 +142,8 @@ func HangupRunningTask(toRunTask string) {
 				break
 
 			} else {
-				//1. Hangup connected PAD
-				Hangup(priority.RunningPATaskChan)
 
-				//2. Redirect all the other pads in 0301 to 0300
+				//1. Redirect all the other pads in 0301 to 0300
 				resCaller, err := QueueStatus("0301", "") // check OCC queue, get entries
 				if err != nil {
 					lfshook.NewLogger().Infof("===QueueStatus==%+v", err)
@@ -152,6 +153,9 @@ func HangupRunningTask(toRunTask string) {
 					time.Sleep(time.Microsecond * 200)
 				}
 
+				//2. Hangup connected PAD
+				Hangup(priority.RunningPATaskChan)
+
 				//3. Hangup OI & ICP
 				HangupIO()
 				HangupAllLocalChan()

+ 103 - 2
internal/app/ami/action/index.go

@@ -1,6 +1,8 @@
 package action
 
 import (
+	"fmt"
+	"os"
 	"pbx-api-gin/internal/app/stc/active"
 	"pbx-api-gin/internal/app/stc/priority"
 	alstatus "pbx-api-gin/internal/app/stc/sendstatus"
@@ -18,6 +20,7 @@ import (
 )
 
 var AminInstance *amigo.Amigo
+var trainInfo = ""
 
 func HandleAMI(event map[string]string) {
 	//lfshook.NewLogger().Infof("===start======%s", event["Event"]
@@ -54,8 +57,102 @@ 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
-			alstatus.SendRecordFile(event["FILENAME"], event["RecordType"])
+		//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"])
+					break
+				} else if os.IsNotExist(err) {
+					lfshook.NewLogger().Infof("File not found (attempt %d): %s", i+1, event["FILENAME"])
+				} else {
+					lfshook.NewLogger().Infof("Error checking file: %v", err)
+				}
+			}
+
+			if !fileExists { //5秒内没有生成录音文件
+				lfshook.NewLogger().Infof("File %s not found after 5 attempts", event["FILENAME"])
+				break
+			}
+
+			//获取录音文件时长,检测录音文件是否超过3min;
+			duration, err := utils.GetDuration(event["FILENAME"])
+			if err != nil {
+				utils.Logger.Printf("Get duration err: %+v", err)
+				break
+			}
+			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 := ""
+			if len(FileNames) > 0 { // 文件切割之后进入循环处理
+				for _, file := range FileNames {
+					DstFile = fmt.Sprintf("encrypted-%s", file)
+
+					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
@@ -76,6 +173,10 @@ func HandleAMI(event map[string]string) {
 				if priority.CheckPriority("CPA") {
 					if active.ActivedCab == "" { //No active Signal on both side,Hangup caller
 						Hangup(event["CallerIDNum"])
+					} else if active.ActivedCab == "1" && event["CallerIDNum"] == "1411" {
+						Hangup(event["CallerIDNum"])
+					} else if active.ActivedCab == "8" && event["CallerIDNum"] == "1481" {
+						Hangup(event["CallerIDNum"])
 					}
 
 					//hangup others if priority is higher

+ 3 - 0
internal/app/index.go

@@ -65,4 +65,7 @@ func SetMasterCabNum() {
 		active.CabNum = "1"
 		active.Master = true
 	}
+
+	active.CabNum = "1"
+	active.Master = true
 }

+ 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
 }

+ 145 - 20
pkg/utils/file.go

@@ -1,15 +1,19 @@
 package utils
 
 import (
+	"encoding/base64"
+	"fmt"
+	"io"
+	"log"
 	"os"
-
-	"github.com/sirupsen/logrus"
-	"gopkg.in/ini.v1"
+	"os/exec"
+	"path/filepath"
+	"pbx-api-gin/internal/pkg/configs"
+	"pbx-api-gin/pkg/lfshook"
+	"strconv"
+	"strings"
 )
 
-// 缓存 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 {
@@ -20,23 +24,144 @@ func FileExists(filename string) bool {
 	return !info.IsDir()
 }
 
-// pbx 不能正确读取时区, 读取自定义文件  /etc/asterisk/ntp.conf
-func GetLocationName() string {
-	if timeZoneCache != "" {
-		return timeZoneCache
+func GetDuration(filePath string) (int, error) {
+	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
+}
+
+func AudioFileEncode(dstFile, srcFile string) error {
+	// Step 1:获取 Base64 编码结果
+	data := "sripis123"
+	encoded := base64.StdEncoding.EncodeToString([]byte(data))
+
+	// Step 2: 创建一个新文件,写入 Base64 编码结果
+	outputFile, err := os.Create(dstFile)
+	if err != nil {
+		return err
+	}
+	defer outputFile.Close()
+
+	_, err = outputFile.WriteString(encoded)
+	if err != nil {
+		return err
+	}
+
+	// Step 3: 打开 audio.wav 文件
+	audioFile, err := os.Open(srcFile)
+	if err != nil {
+		return err
+	}
+	defer audioFile.Close()
+
+	// Step 4: 读取源文件的全部内容
+	data1, err := io.ReadAll(audioFile)
+	if err != nil {
+		return err
+	}
+
+	// Step 5: 对读取的内容进行 Base64 编码
+	encoded1 := base64.StdEncoding.EncodeToString(data1)
+
+	// Step 6: 写入 Base64 编码后的内容
+	_, err = outputFile.WriteString(encoded1)
+	if err != nil {
+		return err
+	}
+	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)
 	}
-	filePath := "/etc/asterisk/ntp.conf"
-	_, err := os.Stat(filePath)
+	// 解析输出,提取所有生成的文件名
+	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 {
-		logrus.Error(err)
-		return ""
+		log.Fatal("模式匹配错误:", err)
 	}
-	iniFile, err := ini.Load(filePath)
+
+	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 {
-		logrus.Error(err)
-		return ""
+		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
 	}
-	timeZoneCache = iniFile.Section("ntp").Key("TZNAME").Value()
-	logrus.Infof("use location %s", timeZoneCache)
-	return timeZoneCache
+
+	return ret[1], ret[2], ret[3]
 }