1
0

34 Commits 5fb01dc065 ... 09621e8a81

Autor SHA1 Mensagem Data
  root 09621e8a81 update há 1 mês atrás
  root 105576a592 修正任务同时触发异常的问题 há 1 mês atrás
  root 992f985acb update há 1 mês atrás
  root 04533d29fa update delay há 1 mês atrás
  dujunchen d93a1c4884 update cpa status há 1 mês atrás
  dujunchen e726595eeb update log há 1 mês atrás
  dujunchen 5cee75053c update há 1 mês atrás
  dujunchen b3e5ee1c16 update há 1 mês atrás
  dujunchen 67965f953e vscode há 1 mês atrás
  dujunchen c6e9d74eab update log há 1 mês atrás
  dujunchen ddf5ec1788 update debug log há 1 mês atrás
  dujunchen f9f2bb662d update há 2 meses atrás
  dujunchen 52fec23e1d update há 2 meses atrás
  dujunchen ba9ac4d666 update há 2 meses atrás
  dujunchen 6d3617e10e update há 2 meses atrás
  dujunchen acc73ffaab update há 2 meses atrás
  dujunchen 2d641ebd54 update há 2 meses atrás
  dujunchen d1928fb7ae update há 2 meses atrás
  dujunchen e0be0df67e update há 2 meses atrás
  dujunchen 3ee25ef53f update há 2 meses atrás
  dujunchen d0eea2ffdf update há 2 meses atrás
  dujunchen 3bec4cf481 update há 2 meses atrás
  dujunchen 709bf023f3 fix PAD record log há 2 meses atrás
  dujunchen 4eb0767e3a update há 2 meses atrás
  dujunchen 89ca76678b 列车信息获取 há 2 meses atrás
  dujunchen c90d32df34 update há 3 meses atrás
  dujunchen 1ccbb5e659 update há 3 meses atrás
  dujunchen d94e83dc39 update log há 3 meses atrás
  dujunchen d02f3bddaf update há 3 meses atrás
  dujunchen ae4c6dd24b update há 3 meses atrás
  dujunchen 35c5543aaa update há 4 meses atrás
  dujunchen 2cee0b4c90 update há 4 meses atrás
  dujunchen f64fc9de04 update há 4 meses atrás
  dujunchen 1f5f8cd83d update há 4 meses atrás

+ 2 - 1
.vscode/settings.json

@@ -1,3 +1,4 @@
 {
-    "git.ignoreLimitWarning": true
+    "git.ignoreLimitWarning": true,
+    "makefile.configureOnOpen": false
 }

+ 38 - 0
1.ini

@@ -0,0 +1,38 @@
+1.PA 打断(检查优先级,通过之后结束当前任务------一个报警和多个报警)
+CPA:挂断当前CPA任务
+
+PAD-ICP:hold已经连接的PAD,超时计时器不启动,当PA挂断时启动超时30秒计时器(再次hold PAD时将PAD插入队首,超时先被转到OCC)
+PAD-TMS:hold PAD(同上)
+PAD-OCC:挂断已经连接的PAD,hold其他PAD,超时计时器不启动,当PA挂断时启动超时0秒计时器
+
+
+2.司机对讲打断(检查优先级,通过之后挂对端ICP------一个报警和多个报警)
+CPA:挂端对端ICP
+
+PAD-ICP:hold已经连接的PAD,超时计时器不启动,当cabcab挂断的时候启动计时器(再次hold PAD时将PAD插入队首,超时先被转到OCC)
+PAD-TMS:(同上)
+PAD-OCC:挂断对端ICP,司机对讲挂断之后将ICP恢复到OCC-PAD状态 
+
+
+司机对讲发起打断其他低优先级或者和其他低优先级共同执行时,暂存之前任务的优先级标记,待到司机对讲结束之后再设置回。
+EMG---ICP
+OPA---ICP
+CPA---ICP
+
+
+
+
+
+
+3.ICP端优先级机制
+
+打断报警的时候发送DTMF,延时500ms再发起呼叫
+打断其他业务则直接挂断,再发起呼叫
+
+
+4.CPA和EMG被打断之后,当高优先级结束之后恢复到CPA或EMG状态,CPA 和EMG的各种状态维持问题,如何恢复EMG的优先级状态################################
+EMG :  confID + runningpriority+ runingtype
+CPA :  
+
+
+5.异常处理,新开一个线程监听AMI事件,根据业务类型判断终端是否是异常状态,如果终端中途掉异常退出则尝试重新建立业务

+ 1 - 1
Makefile

@@ -14,7 +14,7 @@ release-32:
 swagger:
 	swag init -g ../internal/app/http_server/swagger/swagger.go --dir ./api --exclude ./api/admin --output ./web/swagger
 
-release-panel-arm: 
+release-arm: 
 	GOOS=linux GOARCH=arm go build -tags "panel" -ldflags "-X 'main.buildDateTime=$(buildDateTime)' -X 'main.gitCommitCode=$(gitCommitCode)' -X 'main.goVersion=${goVersion}' -s -w" -o ./deployments/pbx-panel-arm ./cmd/main.go
 	
 apidoc:

+ 5 - 4
cmd/main.go

@@ -20,7 +20,6 @@ func main() {
 	initVersion()
 
 	// 解析配置文件
-	configs.ConfigPath = "/data/test/configs/config.yaml"
 	configs.DecodeConfig()
 
 	configs.ConfigGlobal.LogLevel = logrus.InfoLevel
@@ -28,8 +27,6 @@ func main() {
 	//gin.SetMode(gin.ReleaseMode)
 	//gin.SetMode(gin.DebugMode)
 
-	lfshook.NewLogger().Logger.SetReportCaller(true)
-
 	lfshook.NewLogger().Logger.SetFormatter(&logrus.TextFormatter{
 		ForceQuote:      false,
 		FullTimestamp:   true,
@@ -60,10 +57,13 @@ func main() {
 				DisableColors:   true,
 				ForceQuote:      true,
 				TimestampFormat: "2006-01-02 15:04:05",
+				PadLevelText:    false,
 			},
 		))
-	}
+		lfshook.NewLogger().Logger.SetReportCaller(true)
 
+	}
+	utils.InitLog()
 	app.StartApp()
 	utils.Exit()
 	//return nil
@@ -72,6 +72,7 @@ func main() {
 func initVersion() {
 	if gitCommitCode != "" {
 		//构建信息
+		//utils.Logger.Printf("software version: V10.01")
 		lfshook.NewLogger().Printf("git commit code: %s", gitCommitCode)
 		lfshook.NewLogger().Printf("build date: %s", buildDateTime)
 		lfshook.NewLogger().Printf("go version: %s", goVersion)

+ 571 - 128
internal/app/ami/action/call.go

@@ -5,11 +5,12 @@ import (
 	"fmt"
 	"pbx-api-gin/internal/app/stc/active"
 	"pbx-api-gin/internal/app/stc/priority"
+	alstatus "pbx-api-gin/internal/app/stc/sendstatus"
 	"pbx-api-gin/pkg/lfshook"
 	"pbx-api-gin/pkg/utils"
 	"sort"
-	"strconv"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -19,35 +20,32 @@ var Pads = []string{"2413", "2414", "2415", "2421", "2422", "2423", "2424", "242
 
 var Pacus = []string{"2111", "2121", "2131", "2141", "2151", "2161", "2171", "2181"}
 
+var Speakers = []string{"2111", "2121", "2131", "2141", "2151", "2161", "2171", "2181", "2311", "2381"}
+
 // Function triggered before no cab occupied signal interrupt
 func InActiveHangup() {
 	if active.ActivedCab == "" {
-		switch priority.RunningType {
-		case "PA":
-			HangupRunningTask("InActiveHangup")
-		case "CPA":
-			HangupRunningTask("InActiveHangup")
-		case "VOL":
-			HangupRunningTask("InActiveHangup")
-		case "PAD-OCC":
-			HangupRunningTask("InActiveHangup")
-		}
+		HangupTask("PA")
+		HangupTask("CPA")
+		HangupTask("VOL")
+		HangupTask("PAD-OCC")
+	} else {
+		HangupTask("PA")
+		HangupTask("PAD-OCC")
+		HangupTask("CPA")
+		HangupTask("EMG")
+		HangupTask("SPC")
+		HangupTask("DCS")
+		HangupTask("STN")
+		HangupTask("CHK")
+		HangupTask("VOL")
+		Hangup("2311") //hangup cabcab
 	}
 }
 
-// check EMG resume
-func CheckEmgResume() {
-	/*
-		time.AfterFunc(2*time.Second, func() {
-			if priority.CheckPriority("EMG") || priority.RunningType == "C2C" {
-				PlaybackPacu(strconv.Quote(priority.ResumeEmgPara.FileName), priority.ResumeEmgPara.Count, priority.ResumeEmgPara.Delay, priority.ResumeEmgPara.BroadcastType)
-			}
-		})*/
-}
-
 // Hangup 挂断指定分机或通道
 func Hangup(channel string) {
-	lfshook.NewLogger().Infof("hangup extensions/channel %s", channel)
+	lfshook.NewLogger().Infof("Hangup extensions/channel :%s", channel)
 	if !utils.IsChannel(channel) {
 		channel = fmt.Sprintf(`/^(PJ)?SIP\/%s-.*$/`, channel)
 	}
@@ -56,7 +54,7 @@ func Hangup(channel string) {
 		"Action":  "hangup",
 		"Channel": channel,
 	}
-	lfshook.NewLogger().Infof("hangup action %+v", action)
+	//lfshook.NewLogger().Infof("hangup action :%+v", action)
 	if _, _, err := AminInstance.Send(action); err != nil {
 		lfshook.NewLogger().Errorf("Hangup %+v", err)
 	}
@@ -99,112 +97,265 @@ func HangupAllExcept(caller string) {
 	}
 }
 
-// Hangup all ICP
-func HangupRunningTask(toRunTask string) {
+// Hangup task
+func HangupTask(TaskName string) {
+	lfshook.NewLogger().Infof("HangupTask Check TaskName:%s ", TaskName)
 
-	lfshook.NewLogger().Infof("===HangupRunningTask=toRuntask=%s====   RunningTask:%s===", toRunTask, priority.RunningType)
+	if TaskName == "PAD-OCC" {
+		resCaller, err := QueueStatus("0301", "") // check OCC queue, get entries
+		if err != nil {
+			lfshook.NewLogger().Infof("QueueStatus err:%+v", err)
+			return
+		}
 
-	//same type return
-	if toRunTask == priority.RunningType {
+		//lfshook.NewLogger().Infof("============QueueStatus ret :%+v", resCaller)
+		if resCaller != nil {
+			//lfshook.NewLogger().Infof("===QueueStatus=entry=%+v", resCaller.Entrys)
+			if resCaller.Entrys != nil {
+				for _, caller := range resCaller.Entrys {
+					//lfshook.NewLogger().Infof("===QueueStatus=entry=%+v", caller)
+					time.Sleep(time.Millisecond * 50)
+					RedirectInQueue(caller.CallerIDNum, "0300", "queues-icp-redirect", caller.CallerIDNum) // redirect All PAD redirect to ICP queue
+				}
+			}
+		}
+	}
 
-		if toRunTask == "PAD-ICP" || toRunTask == "PAD-TMS" {
-			lfshook.NewLogger().Infof("===HangupRunningTask=ret==== ")
-			return
+	taskInfo, ok := priority.RegistryTask.Get(TaskName)
+	if ok {
+		HangupAllLocalChan()
+		ConfbridgeKick(taskInfo.ConfbridgeID, "all")
+		Hangup(taskInfo.RunChannel)
+	}
+}
+
+// interrupt the running task
+func InterruptRunningTask(toRunTask string) string {
+	utils.LoggerDebug.Printf("InterruptRunningTask  TorunType:%s", toRunTask)
+
+	var task priority.TaskInfo
+	var taskName string
+	var ok bool
+
+	lfshook.NewLogger().Infof("InterruptRunningTask toRuntask:%s  ", toRunTask)
+
+	if toRunTask != "PA" && toRunTask != "PAD-ICP" && toRunTask != "PAD-TMS" { // ignore C2C
+		taskName, task, ok = priority.RegistryTask.HighestPriorityRunningTask1()
+		if !ok {
+			return ""
+		}
+	} else { // have to check C2C
+		taskName, task, ok = priority.RegistryTask.HighestPriorityRunningTask()
+		if !ok {
+			return ""
+		}
+	}
+	utils.LoggerDebug.Printf("InterruptRunningTask  RunningTask:%+v", taskName)
+	lfshook.NewLogger().Infof("InterruptRunningTask RunningTask:%+v  ", task)
+	//same type return
+	if toRunTask == taskName {
+		if toRunTask == "PAD-ICP" || toRunTask == "PAD-TMS" || toRunTask == "PAD-OCC" {
+
+			//Auto Answer PAD-OCC one by one, clean the old confbridge and channels before answer a new PAD .
+			if toRunTask == "PAD-OCC" {
+				Hangup(task.RunChannel) //pad
+				ConfbridgeKick(task.ConfbridgeID, "all")
+				HangupIO() //io
+				//lfshook.NewLogger().Infof("===InterruptRunningTask=ret==== ")
+			}
+			return taskName
 		}
 	}
 
+	//pad all  reset
 	if toRunTask == "AlarmHoldResetAll" {
-		Hangup(priority.RunningPATaskChan)
-		priority.CleanPriorityTag()
 		HangupAllLocalChan()
-		return
+		return taskName
 	}
 
-	if priority.RunningPATaskChan != "" {
+	switch task.RunType {
+	case "SPC":
+		if toRunTask == "C2C" {
+			HangupICP()
+			//alstatus.PaStatus("", "SPC", "end")
+		} else {
+			ConfbridgeKick(task.ConfbridgeID, "all")
+			alstatus.PaStatus("", "SPC", "end")
+		}
+		time.Sleep(time.Millisecond * 200)
+	case "CHK":
+		if toRunTask == "C2C" {
+			HangupICP()
+			//alstatus.PaStatus("", "CHK", "end")
+		} else {
+			ConfbridgeKick(task.ConfbridgeID, "all")
+			alstatus.PaStatus("", "CHK", "end")
+		}
+		time.Sleep(time.Millisecond * 200)
+	case "DCS":
+		if toRunTask == "STN" {
+			return taskName
+		} else if toRunTask == "C2C" {
+			HangupICP()
+			//alstatus.PaStatus("", "DCS", "end")
+		} else {
+			ConfbridgeKick(task.ConfbridgeID, "all")
+			alstatus.PaStatus("", "DCS", "end")
+		}
 
-		if priority.RunningType == "C2C" { // Interrupt C2C task running,
-			if toRunTask == "PA" || toRunTask == "PAD-ICP" || toRunTask == "PAD-TMS" {
-				HangupICP()
-				//priority.CleanPriorityTag()
-			}
+		time.Sleep(time.Millisecond * 200)
+	case "STN":
+		if toRunTask == "DCS" {
+			return taskName
+		} else if toRunTask == "C2C" {
+			HangupICP()
+			//alstatus.PaStatus("", "STN", "end")
+		} else {
+			ConfbridgeKick(task.ConfbridgeID, "all")
+			alstatus.PaStatus("", "STN", "end")
+		}
+		time.Sleep(time.Millisecond * 200)
+	case "CPA":
+		//kick CPA members
+		if toRunTask != "C2C" {
+			CPAConfbridgeKick(task.ConfbridgeID)
+			alstatus.PaStatus("", "CPA", "end")
+		} else if toRunTask == "C2C" {
+			HangupICP()
+			//alstatus.PaStatus("", "CPA", "end")
+		}
+		time.Sleep(time.Millisecond * 200)
+	case "EMG":
+		//kick EMG members
+		if toRunTask != "C2C" {
+			EMGConfbridgeKick(task.ConfbridgeID)
+			alstatus.PaStatus("", "EMG", "end")
+		} else if toRunTask == "C2C" {
+			HangupICP()
+			//alstatus.PaStatus("", "EMG", "end")
+		}
+		time.Sleep(time.Millisecond * 200)
+	case "C2C": // Interrupt C2C task running,
+		if toRunTask == "PA" || toRunTask == "PAD-ICP" || toRunTask == "PAD-TMS" {
+			HangupICP()
+			return taskName
 		}
 
-		switch priority.RunningType {
+	case "PAD-ICP", "PAD-TMS": // Interrupt PAD task running,
+		if toRunTask == "PAD-ICP" || toRunTask == "PAD-TMS" {
+			break
+		}
 
-		case "PAD-ICP", "PAD-TMS": // Interrupt PAD task running,
-			priority.InterruptedPad = priority.RunningType
-			//just in case
-			if toRunTask == "C2C" { //deal in DTMF event
-				lfshook.NewLogger().Infof("===HangupRunningTask==C2C=return==== ")
-				return
-			}
+		priority.InterruptedPad = "PAD-ICP"
 
-			lfshook.NewLogger().Infof("===HangupRunningTask==hangup===PAD-ICP==%s==== ", priority.RunningType)
-			chans, err := CoreShowChannels()
-			if err != nil {
-				lfshook.NewLogger().Errorf("CoreShowChannels %+v", err)
-			}
+		lfshook.NewLogger().Infof("InterruptRunningTask interrupt PAD-ICP/PAD-TMS ,Running type :%s !", task.RunType)
 
-			//1. Redirect the connected PAD to 0300,hangup the other pad channel
-			for _, ret := range chans {
-
-				if utils.IsPAIU(ret.CallerIDNum) {
-					if ret.ConnectedLineNum == "<unknown>" { //redirect pad chanspy channel
-						//lfshook.NewLogger().Infof("===Redirect=Chan===%+v==== ", ret.Channel)
-						err := Redirect(ret.Channel, "0300", "queues-icp-redirect", "", "PAD")
-						if err != nil {
-							lfshook.NewLogger().Errorf("Redirect %+v", err)
-						}
-
-						number := strings.Split(strings.Split(ret.Channel, "-")[0], "/")[1]
-						active.NotifyPaiu(number, "hold")
-						HangupAllLocalChan()
-					} else if utils.IsPAIU(ret.CallerIDNum) && ret.ChannelStateDesc == "Up" && utils.IsICP(ret.ConnectedLineNum) {
-						lfshook.NewLogger().Infof("===Hangup=Chan===%+v==== ", ret.Channel)
-						Hangup(ret.Channel)
+		chans, err := CoreShowChannels()
+		if err != nil {
+			lfshook.NewLogger().Infof("InterruptRunningTask CoreShowChannels err:%+v", err)
+			return taskName
+		}
+
+		//1. Redirect the connected PAD to 0300,hangup the other pad channel
+		for _, ret := range chans {
+			// Redirect the connected PAD to 0300
+			if utils.IsPAIU(ret.CallerIDNum) {
+				lfshook.NewLogger().Infof("====interrupt PAD ==== %+v ", ret)
+
+				if ret.ConnectedLineNum == "<unknown>" { //redirect pad chanspy channel
+					err := Redirect(ret.Channel, "0300", "queues-icp-redirect", "", "PAD")
+					if err != nil {
+						lfshook.NewLogger().Infof("InterruptRunningTask Redirect err:%+v", err)
+						return taskName
 					}
+					lfshook.NewLogger().Infof("====interrupt PAD =1111=== %+v ", ret)
+					number := strings.Split(strings.Split(ret.Channel, "-")[0], "/")[1]
+					active.NotifyPaiu(number, "hold")
+					//HangupAllLocalChan()
+				}
+			}
+		}
+
+		for _, ret := range chans {
+			//hangup pad call ICP channel
+			if utils.IsPAIU(ret.CallerIDNum) {
+				if utils.IsPAIU(ret.CallerIDNum) && ret.ChannelStateDesc == "Up" && utils.IsICP(ret.ConnectedLineNum) {
+					lfshook.NewLogger().Infof("Hangup PAD Channel :%+v", ret.Channel)
+					Hangup(ret.Channel)
 				}
 			}
+		}
 
-			//2. hangup task channel (ICP + PACU)
-			lfshook.NewLogger().Infof("===HangupRunningTask=2. hangup task channel === ")
-			HangupAllLocalChan()
-			//HangupICP()
-			priority.CleanPriorityTag()
+		priority.RegistryTask.StopAndUnregister("PAD-ICP")
+		priority.RegistryTask.StopAndUnregister("PAD-TMS")
+		//2. hangup task channel (ICP + PACU)
+		//lfshook.NewLogger().Infof("===InterruptRunningTask=2. hangup task channel === ")
+		HangupAllLocalChan()
+		HangupICP()
 
-		case "PAD-OCC": // Interrupt PAD-OCC task running,
+		//pad end
+		if priority.PADStart == 1 {
+			alstatus.PaStatus("", "PAD", "end")
+			priority.PADStart = 0
+		}
 
-			if toRunTask == "C2C" {
-				break
+	case "PAD-OCC": // Interrupt PAD-OCC task running,
 
-			} else {
-				priority.InterruptedPad = priority.RunningType
-				//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)
-				}
+		if toRunTask == "C2C" {
+			HangupICP()
+			break
+
+		} else {
+			priority.InterruptedPad = "PAD-OCC"
+			priority.OCCAnswer = 0
+
+			resCaller, err := QueueStatus("0301", "") // check OCC queue, get entries
+			if err != nil {
+				lfshook.NewLogger().Infof("QueueStatus err:%+v", err)
+				return taskName
+			}
+
+			if resCaller == nil {
+				return taskName
+			}
+
+			if resCaller.Entrys != nil {
 				for _, caller := range resCaller.Entrys {
-					go RedirectInQueue(caller.CallerIDNum, "0300", "queues-icp-redirect", caller.CallerIDNum) // redirect All PAD redirect to ICP queue
-					time.Sleep(time.Microsecond * 200)
+					//lfshook.NewLogger().Infof("===QueueStatus=entry=%+v", caller)
+					time.Sleep(time.Millisecond * 50)
+					RedirectInQueue(caller.CallerIDNum, "0300", "queues-icp-redirect", caller.CallerIDNum) // redirect All PAD redirect to ICP queue
 				}
+			}
+
+			priority.RegistryTask.StopAndUnregister("PAD-OCC")
+			//2. Hangup connected PAD
+			//lfshook.NewLogger().Infof("===InterruptRunningTask=Hangup connected PAD=== ")
+			Hangup(task.RunChannel)
 
-				//2. Hangup connected PAD
-				lfshook.NewLogger().Infof("===HangupRunningTask=Hangup connected PAD=== ")
-				Hangup(priority.RunningPATaskChan)
+			//3. Hangup OI & ICP
+			HangupIO()
+			HangupAllLocalChan()
+			ConfbridgeKick(task.ConfbridgeID, "all")
 
-				//3. Hangup OI & ICP
-				HangupIO()
-				HangupAllLocalChan()
-				priority.CleanPriorityTag()
+			//occ pad end
+			if priority.PADOccStart == 1 {
+				alstatus.OccPad("end")
+				priority.PADOccStart = 0
 			}
 
-		default:
-			lfshook.NewLogger().Infof("===HangupRunningTask=default=== ")
-			Hangup(priority.RunningPATaskChan)
-			priority.CleanPriorityTag()
+		}
+
+	default:
+		lfshook.NewLogger().Infof("InterruptRunningTask goto default !")
+		//if !strings.Contains(priority.RunningPATaskChan, "0502@default") {
+		//	Hangup(priority.RunningPATaskChan)
+		//}
+		if toRunTask != "C2C" && task.RunType != "PA" {
+			Hangup(task.RunChannel)
+			ConfbridgeKick(task.ConfbridgeID, "all")
 		}
 	}
+	return taskName
 }
 
 // Hangup all ICP
@@ -230,10 +381,13 @@ func HangupAllPACU() {
 func HangupAllLocalChan() {
 	chans, err := CoreShowChannels()
 	if err != nil {
-		lfshook.NewLogger().Errorf("CoreShowChannels %+v", err)
+		lfshook.NewLogger().Infof("CoreShowChannels %+v", err)
+		return
 	}
+
 	for _, ret := range chans {
-		if strings.Contains(ret.Channel, "Local") {
+		if strings.Contains(ret.Channel, "Local") && !strings.Contains(ret.Channel, "0502@default") {
+			//lfshook.NewLogger().Infof("HangupAllLocalChan=====hangup========= %+v", ret)
 			Hangup(ret.Channel)
 		}
 	}
@@ -265,7 +419,7 @@ func Dial(src, dst, dialrule, callerID, callerName string, callType string) {
 		"Variable": fmt.Sprintf("CAB=%s", callType),
 		"async":    "true",
 	}
-	lfshook.NewLogger().Infof("dial action %+v", action)
+	lfshook.NewLogger().Infof("================dial action %+v", action)
 	res, _, err := AminInstance.Send(action)
 	if err != nil {
 		lfshook.NewLogger().Errorf("%+v", err)
@@ -346,7 +500,7 @@ func ChanSpy(src, dst string, whisper, bargein bool) {
 // Redirect 转接
 func RedirectInQueue(channel, dst, dialrule, callerID string) (err error) {
 	//callerID := "redirect"
-	lfshook.NewLogger().Infof("redirect src %s to dst %s", channel, dst)
+	//lfshook.NewLogger().Infof("redirect src %s to dst %s", channel, dst)
 	if !utils.IsChannel(channel) {
 		//callerID = channel
 		if channel, err = GetChannelByExtenNotBridged(channel); err != nil {
@@ -363,7 +517,7 @@ func RedirectInQueue(channel, dst, dialrule, callerID string) (err error) {
 		"Priority": "1",
 		"async":    "true",
 	}
-	lfshook.NewLogger().Infof("redirect %+v", action)
+	lfshook.NewLogger().Infof("RedirectInQueue: %+v", action)
 	res, _, err := AminInstance.Send(action)
 	if err != nil {
 		lfshook.NewLogger().Error(err)
@@ -375,7 +529,7 @@ func RedirectInQueue(channel, dst, dialrule, callerID string) (err error) {
 // Redirect 转接
 func Redirect(channel, dst, dialrule, callerID, callerName string) (err error) {
 	//callerID := "redirect"
-	lfshook.NewLogger().Infof("redirect src %s to dst %s", channel, dst)
+	//lfshook.NewLogger().Infof("redirect src %s to dst %s", channel, dst)
 	if !utils.IsChannel(channel) {
 		callerID = channel
 		if channel, err = GetChannelByExten(channel); err != nil {
@@ -392,7 +546,7 @@ func Redirect(channel, dst, dialrule, callerID, callerName string) (err error) {
 		"Priority": "1",
 		"async":    "true",
 	}
-	lfshook.NewLogger().Infof("redirect %+v", action)
+	lfshook.NewLogger().Infof("Redirect: %+v", action)
 	res, _, err := AminInstance.Send(action)
 	if err != nil {
 		lfshook.NewLogger().Error(err)
@@ -403,52 +557,97 @@ func Redirect(channel, dst, dialrule, callerID, callerName string) (err error) {
 
 func SetPadTimer() {
 
-	toRunpriority, _ := strconv.Atoi(priority.GetPriorityByKey(priority.InterruptedPad)) // 获取之前打断的报警优先级
-	lfshook.NewLogger().Infof("==SetPadTimer====runing:%d==toRun:%d=", priority.RunningTypePriority, toRunpriority)
-
-	if priority.RunningTypePriority < toRunpriority || priority.RunningTypePriority == 0 {
+	toRunPadpriority := priority.GetPriorityByKey(priority.InterruptedPad) //Get PAD priori 获取之前打断的报警优先级
+	//toRunpriority := priority.GetPriorityByKey("PAD-ICP")
 
-		res, err := QueueStatus("0300", "") // check OCC queue , if empty OCC-PAD start
-		if err != nil {
-			lfshook.NewLogger().Infof("===OCC-QueueStatus==%+v", err)
+	_, taskTmp, ok := priority.RegistryTask.HighestPriorityRunningTask()
+	if ok {
+		lfshook.NewLogger().Infof("PAD SetPadTimer runing priority:%d toRun priority:%d", taskTmp.Priority, toRunPadpriority)
+		if taskTmp.Priority < toRunPadpriority { //higher priority task running ,do not set timer
 			return
 		}
-		if res.Calls != "0" {
+	}
 
-			lfshook.NewLogger().Infof("==SetPadTimer==QueueTimer===1====")
-			active.QueueTimer = time.AfterFunc(30*time.Second, func() { // check the PAD 30s timeout
+	res, err := QueueStatus("0300", "") // check OCC queue , if empty OCC-PAD start
+	if err != nil {
+		lfshook.NewLogger().Infof("QueueStatus err%+v", err)
+		return
+	}
+	if res == nil {
+		return
+	}
+	if res.Calls != "0" {
+
+		lfshook.NewLogger().Infof("PAD SetPadTimer Set QueueTimer timeout 30s !")
+		//active.SetTimer = true
+		//lfshook.NewLogger().Logger.Infof("=========Start PAD timer !=============")
+		if active.QueueTimer != nil {
+			if active.QueueTimer.Stop() {
+				lfshook.NewLogger().Logger.Infof("=========Release PAD timer true !============")
+			} else {
+				lfshook.NewLogger().Logger.Infof("=========Release PAD timer false ! ============")
+			}
+		}
+		lfshook.NewLogger().Logger.Infof("=========Start PAD timer !======%d=======", active.PADTimeout)
+		active.QueueTimer = time.AfterFunc(time.Duration(active.PADTimeout)*time.Second, func() { // check the PAD 30s timeout
+			//active.QueueTimer = time.AfterFunc(30*time.Second, func() { // check the PAD 30s timeout
+			//if both not active , return
+			if active.ActivedCab == "" {
+				return
+			}
 
-				res, err := QueueStatus("0301", "") // check OCC queue , if empty OCC-PAD start
+			res, err := QueueStatus("0301", "") // check OCC queue , if empty OCC-PAD start
+			if err != nil {
+				lfshook.NewLogger().Infof("OCC QueueStatus err:%+v", err)
+				return
+			}
+
+			if res == nil {
+				return
+			}
+
+			if res.Calls == "0" { // OCC queue empty
+				resCaller, err := QueueStatus("0300", "") // check ICP queue, get entries
 				if err != nil {
-					lfshook.NewLogger().Infof("===OCC-QueueStatus==%+v", err)
+					lfshook.NewLogger().Infof("ICP QueueStatus err:%+v", err)
 					return
 				}
 
-				if res.Calls == "0" { // OCC queue empty
-					resCaller, err := QueueStatus("0300", "") // check ICP queue, get entries
-					if err != nil {
-						lfshook.NewLogger().Infof("==ICP=QueueStatus==%+v", err)
-						return
-					}
+				if resCaller.Entrys != nil {
 					sort.Slice(resCaller.Entrys, func(i, j int) bool {
 						return resCaller.Entrys[i].Position < resCaller.Entrys[j].Position
 					})
 
 					for _, caller := range resCaller.Entrys {
 						priority.ICPAnswer = 0
-						lfshook.NewLogger().Infof("====SetPadTimer==QueueTimer==2=")
-						lfshook.NewLogger().Infof("====Redirect to 0301 entry:%s=Pos:%s==", caller.CallerIDNum, caller.Position)
+						//lfshook.NewLogger().Infof("====SetPadTimer==QueueTimer==2=")
+						lfshook.NewLogger().Infof("Q300==SetPadTimer==Redirect to 0301 entry:%s=Pos:%s==", caller.CallerIDNum, caller.Position)
 						//order by pos
 						RedirectInQueue(caller.CallerIDNum, "0301", "queues-occ", caller.CallerIDNum) // redirect All ICP-PAD redirect to OCC queue
-						time.Sleep(time.Microsecond * 500)                                            //200 ms delay
+						time.Sleep(time.Millisecond * 100)                                            //200 ms delay
 					}
 				}
-			})
-		}
+
+				//==============test info =====================
+				/*
+					time.Sleep(2 * time.Second)
+					occque, err1 := QueueStatus("0301", "") // check ICP queue, get entries
+					if err1 != nil {
+						lfshook.NewLogger().Infof("ICP QueueStatus err:%+v", err)
+						return
+					}
+
+					for _, caller := range occque.Entrys {
+						lfshook.NewLogger().Infof("Q301==SetPadTimer==Redirect to 0301 entry:%s=Pos:%s==", caller.CallerIDNum, caller.Position)
+					}*/
+			}
+		})
 	}
+
 }
 
 func ConfbridgeKick(confnum, channel string) (res map[string]string, err error) {
+
 	action := map[string]string{
 		"Action":     "ConfbridgeKick",
 		"Conference": confnum,
@@ -459,9 +658,253 @@ func ConfbridgeKick(confnum, channel string) (res map[string]string, err error)
 	if err != nil {
 		return nil, err
 	}
+	lfshook.NewLogger().Infof("ConfbridgeKick res:%+v", res)
+
 	if res["Response"] != "Success" {
 		return nil, errors.New(res["Message"])
 	}
 
 	return res, nil
 }
+
+func CPAConfbridgeKick(confnum string) (res map[string]string, err error) {
+	chans, err := ConfbridgeList(confnum)
+	if err != nil {
+		return nil, errors.New(res["Message"])
+	}
+
+	for _, confChan := range chans {
+		if !strings.Contains(confChan, "PJSIP/1481") {
+			ConfbridgeKick(confnum, confChan)
+		}
+	}
+	return res, nil
+}
+
+func CPAConfbridgeReinvite(confID string) {
+	utils.LoggerDebug.Printf("CPA CPAConfbridgeReinvite , resume CPA .")
+	time.Sleep(time.Millisecond * 500)
+	for _, ext := range Speakers {
+		ConfbridgeReinvite(ext, "call-speakers-cpa", confID)
+	}
+}
+
+func EMGConfbridgeKick(confnum string) (res map[string]string, err error) {
+
+	chans, err := ConfbridgeList(confnum)
+	if err != nil {
+		return nil, errors.New(res["Message"])
+	}
+
+	for _, confChan := range chans {
+		if !strings.Contains(confChan, "0502@default") {
+			ConfbridgeKick(confnum, confChan)
+		}
+	}
+	return res, nil
+}
+
+func EMGConfbridgeReinvite(confID string) {
+	utils.LoggerDebug.Printf("EMG ConfbridgeReinvite , resume EMG .")
+	time.Sleep(time.Millisecond * 500)
+	for _, ext := range Speakers {
+		ConfbridgeReinvite(ext, "call-speakers-emg", confID)
+	}
+}
+
+func ConfbridgeList(confnum string) (chans []string, err error) {
+
+	action := map[string]string{
+		"Action":     "ConfbridgeList",
+		"Conference": confnum,
+	}
+
+	res, events, err := AminInstance.Send(action)
+	if err != nil {
+		return nil, err
+	}
+	lfshook.NewLogger().Infof("ConfbridgeList res:%+v", res)
+
+	if res["Response"] == "Success" {
+		for _, event := range events {
+			if event.Data["Event"] == "ConfbridgeList" {
+				chans = append(chans, event.Data["Channel"])
+			}
+		}
+		return chans, nil
+	} else {
+		return nil, errors.New(res["Message"])
+	}
+}
+
+func ConfbridgeReinvite(src, context, confID string) {
+	if ExtenStatus(src) != "Idle" {
+		lfshook.NewLogger().Infof(" ConfbridgeReinvite ext:%s Not Idle !", src)
+		return
+	}
+
+	chanel := fmt.Sprintf("Local/%s@%s", src, context)
+	action := map[string]string{
+		"Action":   "Originate",
+		"Channel":  chanel,
+		"Exten":    "000",
+		"Context":  "confbridge-join",
+		"CallerID": fmt.Sprintf("%s<%s>", "", ""),
+		"Priority": "1",
+		"Variable": fmt.Sprintf("CBID=%s", confID),
+		"async":    "true",
+	}
+
+	lfshook.NewLogger().Infof("dial action %+v", action)
+	res, _, err := AminInstance.Send(action)
+	if err != nil {
+		lfshook.NewLogger().Errorf("%+v", err)
+	}
+	lfshook.NewLogger().Info(res)
+}
+
+func ICPConfbridgeReinvite(confID, paType string) {
+
+	switch paType {
+	case "PAD-OCC":
+		go DialICP("8", "2311", "confbridge-join", confID, "1") //ICP1---call
+		go DialICP("8", "2381", "confbridge-join", confID, "8") //ICP8---call
+	case "CPA":
+		go DialICP("2", "2311", "confbridge-join", confID, "1") //ICP1---call
+		go DialICP("2", "2381", "confbridge-join", confID, "8") //ICP8---call
+	case "EMG":
+		go DialICP("3", "2311", "confbridge-join", confID, "1") //ICP1---call
+		go DialICP("3", "2381", "confbridge-join", confID, "8") //ICP8---call
+	case "SPC":
+		go DialICP("6", "2311", "confbridge-join", confID, "1") //ICP1---call
+		go DialICP("6", "2381", "confbridge-join", confID, "8") //ICP8---call
+	case "STN":
+		go DialICP("4", "2311", "confbridge-join", confID, "1") //ICP1---call
+		go DialICP("4", "2381", "confbridge-join", confID, "8") //ICP8---call
+	case "DCS":
+		go DialICP("5", "2311", "confbridge-join", confID, "1") //ICP1---call
+		go DialICP("5", "2381", "confbridge-join", confID, "8") //ICP8---call
+	case "CHK":
+		go DialICP("10", "2311", "confbridge-join", confID, "1") //ICP1---call
+		go DialICP("10", "2381", "confbridge-join", confID, "8") //ICP8---call
+	case "VOL":
+		go DialICP("11", "2311", "confbridge-join", confID, "1") //ICP1---call
+		go DialICP("11", "2381", "confbridge-join", confID, "8") //ICP8---call
+
+	}
+}
+
+var waitMutex sync.Mutex
+
+// 设置全局变量控制任务创建过程,避免被其他任务打乱任务创建过程
+func WaitTaskCreate(task string, args ...string) { //arg1(task chan)
+	utils.LoggerDebug.Printf("%s check task creating ..... ,TaskCreating = %s", task, priority.TaskCreating)
+
+	waitMutex.Lock()
+	defer waitMutex.Unlock()
+
+	switch priority.TaskCreating {
+	case "C2C":
+		if /*task == "PA" ||*/ task == "CPA" {
+			//获取正在创建的任务的优先级
+			priorityC2C := priority.GetPriorityByKey("C2C")
+			//获取将要创建的任务的优先级
+			priorityTask := priority.GetPriorityByKey(task)
+			//比较优先级,确定是否终止正在创建的任务
+
+			if priorityC2C < priorityTask {
+				utils.LoggerDebug.Printf("%s check task C2C creating , hangup CPA %s ", task, args[0])
+
+				//结束task(CPA)
+				if len(args) > 0 {
+					Hangup(args[0])
+					goto DELAY
+				}
+			} else {
+				//结束C2C,PA 则不需要挂断ICP(由ICP自行处理优先级)
+				utils.LoggerDebug.Printf("%s check task C2C creating , hangup C2C ICPs ", task)
+				HangupICP()
+				goto DELAY
+			}
+		}
+		return
+	case "CPA":
+		if task == "PA" /*|| task == "C2C"*/ {
+			//获取正在创建的任务的优先级
+			priorityCPA := priority.GetPriorityByKey("CPA")
+			//获取将要创建的任务的优先级
+			priorityTask := priority.GetPriorityByKey(task)
+			//比较优先级,确定是否终止正在创建的任务
+
+			if priorityCPA < priorityTask {
+				utils.LoggerDebug.Printf("%s check task CPA creating , hangup %s %s", task, task, args[0])
+				//结束task(PA,CPA)
+				if len(args) > 0 {
+					Hangup(args[0])
+					goto DELAY
+				}
+			} else {
+				//结束CPA,获取CPA通道
+				if active.ActivedCab == "1" {
+					utils.LoggerDebug.Printf("%s check task  creating , hangup CPA %s ", task, args[0])
+					Hangup("1481")
+					goto DELAY
+				} else if active.ActivedCab == "8" {
+					utils.LoggerDebug.Printf("%s check task C2C creating , hangup CPA %s ", task, args[0])
+					Hangup("1411")
+					goto DELAY
+				}
+			}
+		}
+		return
+	case "PA":
+		if task == "CPA" /*|| task == "C2C"*/ {
+			//获取正在创建的任务的优先级
+			priorityPA := priority.GetPriorityByKey("PA")
+			//获取将要创建的任务的优先级
+			priorityTask := priority.GetPriorityByKey(task)
+			//比较优先级,确定是否终止正在创建的任务
+
+			if priorityPA < priorityTask {
+				utils.LoggerDebug.Printf("%s check task PA creating , hangup CPA %s ", task, args[0])
+				//结束task(CPA)
+				if len(args) > 0 {
+					Hangup(args[0])
+					goto DELAY
+				}
+			} else {
+				//结束PA,获取PA通道
+				if active.ActivedCab == "1" {
+					utils.LoggerDebug.Printf("%s check task PA creating , hangup PA %s ", task, "2311")
+					Hangup("2311")
+					goto DELAY
+				} else if active.ActivedCab == "8" {
+					utils.LoggerDebug.Printf("%s check task PA creating , hangup PA %s ", task, "2381")
+					Hangup("2381")
+					goto DELAY
+				}
+			}
+		}
+		return
+
+	default:
+		//utils.LoggerDebug.Printf("%s waiting trd=============previous task:%s creating ..... ", task, priority.TaskCreating)
+
+		for i := 0; i < 4; i++ {
+			if priority.TaskCreating != "" {
+				utils.LoggerDebug.Printf("%s waiting previous task:%s creating ..... ", task, priority.TaskCreating)
+				time.Sleep(time.Millisecond * 500)
+			} else {
+				utils.LoggerDebug.Printf("TaskCreating is nill, Set TaskCreating=%s", task)
+				priority.TaskCreating = task
+				return
+			}
+		}
+		priority.TaskCreating = task
+		utils.LoggerDebug.Printf("%s waiting previous task:%s creating timeout ! Set TaskCreating=%s", task, priority.TaskCreating, task)
+		return
+	}
+
+DELAY:
+	time.Sleep(100 * time.Millisecond)
+}

+ 4 - 4
internal/app/ami/action/channel.go

@@ -18,7 +18,7 @@ func CoreShowChannels() (result []model.CoreShowChannelResVO, err error) {
 		return nil, err
 	}
 
-	lfshook.NewLogger().Tracef("events %+v", events)
+	lfshook.NewLogger().Infof("events %+v", events)
 	result = make([]model.CoreShowChannelResVO, 0)
 	for _, event := range events {
 		if event.Data["Event"] == "CoreShowChannel" {
@@ -35,7 +35,7 @@ func CoreShowChannels() (result []model.CoreShowChannelResVO, err error) {
 			result = append(result, channel)
 		}
 	}
-	lfshook.NewLogger().Tracef("channels %+v", result)
+	lfshook.NewLogger().Infof("channels %+v", result)
 	return result, nil
 }
 
@@ -79,7 +79,7 @@ func GetChannelByExtenNotBridged(exten string) (channel string, err error) {
 	}
 
 	for _, event := range events {
-		lfshook.NewLogger().Infof("CoreShowChannels event Data %+v", event.Data)
+		//lfshook.NewLogger().Infof("CoreShowChannels event Data %+v", event.Data)
 		if event.Data["Event"] == "CoreShowChannel" && event.Data["CallerIDNum"] == exten {
 			channel = event.Data["Channel"]
 			lfshook.NewLogger().Infof("GetChannelByExten get channel %s", channel)
@@ -106,7 +106,7 @@ func GetExtenChan(exten string) (channel string, err error) {
 	}
 
 	for _, event := range events {
-		lfshook.NewLogger().Infof("CoreShowChannels event Data %+v", event.Data)
+		//	lfshook.NewLogger().Infof("CoreShowChannels event Data %+v", event.Data)
 		if event.Data["Event"] == "CoreShowChannel" && event.Data["ConnectedLineNum"] != exten && event.Data["CallerIDNum"] == exten {
 			channel = event.Data["Channel"]
 			lfshook.NewLogger().Infof("GetChannelByExten get channel %s", channel)

Diff do ficheiro suprimidas por serem muito extensas
+ 953 - 454
internal/app/ami/action/index.go


+ 1 - 1
internal/app/ami/action/playback.go

@@ -38,7 +38,7 @@ func PlaybackPacu(filename string, count int, delay int, PaType string) (err err
 		"Variable": Para,
 	}
 
-	lfshook.NewLogger().Logger.Infof("===action : %+v", action)
+	lfshook.NewLogger().Logger.Infof("PlaybackPacu action : %+v", action)
 
 	res, _, err := AminInstance.Send(action)
 	if err != nil {

+ 29 - 29
internal/app/index.go

@@ -1,6 +1,7 @@
 package app
 
 import (
+	"os/exec"
 	"pbx-api-gin/internal/app/ami/action"
 	"pbx-api-gin/internal/app/stc"
 	"pbx-api-gin/internal/app/stc/active"
@@ -8,17 +9,30 @@ import (
 	"pbx-api-gin/internal/app/stc/socket"
 	"pbx-api-gin/pkg/lfshook"
 	"pbx-api-gin/pkg/utils"
+	"syscall"
 	"time"
 )
 
 func StartApp() {
-	//init mysql
-	//mysql.CreateDBInstance()
+
+	//init asterisk
+	if !utils.CheckAsterisk() {
+		lfshook.NewLogger().Infof("Check asterisk , if not running , run cmd service asterisk start !")
+		cmd := exec.Command("service", "asterisk", "start")
+		cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+		err := cmd.Run()
+		if err != nil {
+			lfshook.NewLogger().Infof("Failed to start asterisk: %v", err)
+			return
+		}
+
+		time.Sleep(3 * time.Second)
+	}
 
 	//Get cab number acording to IP
-	SetMasterCabNum()
+	socket.SetMasterCabNum()
 
-	lfshook.NewLogger().Infof("=================cab number:%s========Master:%+v===", active.CabNum, active.Master)
+	utils.LoggerDebug.Printf("Get CabNumber:%s  Init Master:%+v", active.CabNum, active.Master)
 
 	//init the active status
 	//active.ActivedCab = ""
@@ -26,6 +40,9 @@ func StartApp() {
 	//get priority
 	priority.GetPriority()
 
+	//初始化列车信息map 列车号+车厢号
+	active.InitTrainInfoMap()
+
 	// 启动带有重连机制的连接管理协程MC1
 	go stc.StartStcConnection(socket.Conn, "1")
 
@@ -33,39 +50,22 @@ func StartApp() {
 	go stc.StartStcConnection(socket.Conn8, "8")
 
 	//启动连接到Master服务器,检查Master是否在线
-	//if active.CabNum == "8" {
-	//	socket.ConnectedMaster = false
-	//	go stc.StartConnectionToSipServer(socket.ConnToMaster)
+	//if active.CabNum == "1" {
+	//	go stc.StartConnectionToSipServer(socket.ConnToSlave)
+	//} else {
+	//	go stc.RecvFromSipServer()
 	//}
+
 	// 启动其他服务...
 	// 启动 AMI
 	go func() {
 		action.StartAMI(func() {
-			lfshook.NewLogger().Info("ami callback")
+			utils.LoggerDebug.Printf("AMI callback Start .")
 		}, []func(event map[string]string){})
 	}()
 
 	//refresh extension status
-	time.Sleep(3 * time.Second)
-	utils.ExecCmdAsync("/usr/sbin/asterisk", "-rx", "reload")
-}
-
-// Get eth0 IP
-func SetMasterCabNum() {
-
-	ext, err := socket.IsIPExists("10.0.11.11")
-	if err != nil {
-		lfshook.NewLogger().Infof("Check IP :%+v", err)
-	}
-	// Init cab number and master role
-	if !ext {
-		active.CabNum = "8"
-		active.Master = false
-	} else {
-		active.CabNum = "1"
-		active.Master = true
-	}
+	time.Sleep(1 * time.Second)
 
-	active.CabNum = "1"
-	active.Master = true
+	utils.ExecCmdAsync("/usr/sbin/asterisk", "-rx", "reload")
 }

+ 109 - 7
internal/app/stc/active/index.go

@@ -1,22 +1,69 @@
 package active
 
 import (
+	"encoding/xml"
 	"fmt"
 	"net/http"
-	"pbx-api-gin/pkg/lfshook"
+	"os"
+	"pbx-api-gin/pkg/utils"
 	"time"
 )
 
 // var ActiveCab string
 var CabNum string
 var ActivedCab = "1"
-
+var PADTimeout = 30
 var Master = false
 
-var QueueTimer *time.Timer = nil
+var TrainDevide = 0
+var RadioFault = 0
+
+var TrainInfoMap = make(map[string]map[int]string)
+
+// 初始化所有列车号
+var TrainNumGr = []string{"TS1", "TS2", "TS3", "TS4", "TS5", "TS6", "TS7", "TS8", "TS9", "TS10", "TS11", "TS12", "TS13"}
+var TrainNum = "" //STC 设置该变量,可对应取车厢号
+
+// 初始化所有车厢号
+var data = [][]string{
+	{"D433", "P433", "M433", "K433", "C434", "M434", "P434", "D434"},
+	{"D435", "P435", "M435", "K435", "C436", "M436", "P436", "D436"},
+	{"D437", "P437", "M437", "K437", "C438", "M438", "P438", "D438"},
+	{"D439", "P439", "M439", "K439", "C440", "M440", "P440", "D440"},
+	{"D441", "P441", "M441", "K441", "C442", "M442", "P442", "D442"},
+	{"D443", "P443", "M443", "K443", "C444", "M444", "P444", "D444"},
+	{"D445", "P445", "M445", "K445", "C446", "M446", "P446", "D446"},
+	{"D447", "P447", "M447", "K447", "C448", "M448", "P448", "D448"},
+	{"D449", "P449", "M449", "K449", "C450", "M450", "P450", "D450"},
+	{"D451", "P451", "M451", "K451", "C452", "M452", "P452", "D452"},
+	{"D453", "P653", "M453", "K453", "C454", "M454", "P454", "D454"},
+	{"D455", "P455", "M455", "K455", "C456", "M456", "P456", "D456"},
+	{"D457", "P457", "M457", "K457", "C458", "M458", "P458", "D458"},
+}
+
+// Pacu 表示单个 pacu 节点
+type Pacu struct {
+	ID     string `xml:"id"`
+	Status string `xml:"status"`
+	Volume string `xml:"volume"`
+	Mute   bool   `xml:"mute"`
+}
 
-//ICP join PAD-OCC confbridge id
-var CONBID = ""
+// Icp 表示单个 icp 节点
+type Icp struct {
+	ID     string `xml:"id"`
+	Volume string `xml:"volume"`
+}
+
+type Device struct {
+	PacuInfo [8]Pacu `xml:"-"` // 不自动序列化,由自定义方法处理
+	ICPInfo  [2]Icp  `xml:"-"`
+}
+
+var DeviceEndpoint Device
+
+// var SetTimer = false
+var QueueTimer *time.Timer = nil
 
 // 挂断所有报警器
 func NotifyPaiu(Exten, Action string) {
@@ -30,10 +77,10 @@ func NotifyPaiu(Exten, Action string) {
 		url = fmt.Sprintf("http://10.0.24.%s/api/sipphone?action=hangup", Exten[2:])
 	}
 
-	lfshook.NewLogger().Logger.Infof("======Notify PAIU Alarm====:%+v ", url)
+	utils.LoggerDebug.Printf("Notify PAD URL:%+v ", url)
 	resp, err := http.Get(url)
 	if err != nil {
-		lfshook.NewLogger().Logger.Infof("======Notify PAIU Alarm====:%+v ", err)
+		//lfshook.NewLogger().Logger.Infof("======Notify PAIU Alarm====:%+v ", err)
 		return
 	}
 	defer resp.Body.Close()
@@ -50,3 +97,58 @@ func NotifyPaiu(Exten, Action string) {
 	   fmt.Printf("响应内容: %s\n", body)
 	*/
 }
+
+// MarshalXML 实现自定义 XML 输出:按顺序写入所有 pacu 和 icp 节点
+func (d Device) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	// 写入根标签 <device>
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "device"}}); err != nil {
+		return err
+	}
+
+	// 写入所有 <pacu> 节点
+	for _, p := range DeviceEndpoint.PacuInfo {
+		if err := e.Encode(p); err != nil {
+			return err
+		}
+	}
+
+	// 写入所有 <icp> 节点
+	for _, i := range DeviceEndpoint.ICPInfo {
+		if err := e.Encode(i); err != nil {
+			return err
+		}
+	}
+
+	// 写入根结束标签 </device>
+	return e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "device"}})
+}
+
+func GenerateXML(pt string) error {
+	// 创建文件(会覆盖同名文件)
+	file, err := os.Create(pt)
+	if err != nil {
+		//fmt.Printf("创建文件失败: %v\n", err)
+		return err
+	}
+	defer file.Close() // 确保关闭
+
+	// 使用 xml.Encoder 写入(更规范,支持缩进、处理特殊字符等)
+	encoder := xml.NewEncoder(file)
+	encoder.Indent("", "  ") // 启用缩进,等效于 MarshalIndent
+	if err := encoder.Encode(DeviceEndpoint); err != nil {
+		//fmt.Printf("XML 编码写入失败: %v\n", err)
+		return err
+	}
+	return err
+}
+
+func InitTrainInfoMap() {
+	utils.LoggerDebug.Printf("Init train info map ..... ")
+
+	for i, train := range TrainNumGr {
+		TrainInfoMap[train] = make(map[int]string)
+		for j, car := range data[i] {
+			TrainInfoMap[train][j+1] = car //位置从1开始
+		}
+	}
+}

+ 447 - 237
internal/app/stc/broadcast/stc-broadcast.go

@@ -3,20 +3,26 @@ package broadcast
 import (
 	"bytes"
 	"context"
+	"encoding/binary"
 	"fmt"
 	"io"
 	"net"
+	"os/exec"
 	"pbx-api-gin/internal/app/ami/action"
 	"pbx-api-gin/internal/app/stc/active"
 	msgdata "pbx-api-gin/internal/app/stc/data"
 	"pbx-api-gin/internal/app/stc/priority"
 	alstatus "pbx-api-gin/internal/app/stc/sendstatus"
 	"pbx-api-gin/pkg/lfshook"
+	"pbx-api-gin/pkg/utils"
 	"strconv"
 	"sync"
+	"syscall"
 	"time"
 )
 
+var tagLog = 0
+
 func HandleStcCmd(ctx context.Context, conn net.Conn) {
 
 	for {
@@ -38,15 +44,96 @@ func HandleStcCmd(ctx context.Context, conn net.Conn) {
 				}
 				buf.Write(tmp[:n])
 			}
-
+			//lfshook.NewLogger().Logger.Infof("buf:%x==============================", buf.Bytes())
 			for {
 				packet, err := msgdata.ExtractPacket(&buf)
 				if err != nil {
+					utils.LoggerDebug.Printf("Parse error: %v, resetting buffer", err)
+					buf.Reset() // 解析失败,清空避免污染
 					break
 				}
+				if packet == nil {
+					break // 当前无完整包,等待下次 ReadFrom
+				}
+				//处理 packet...
 				go processPacket(packet)
 			}
+
+		}
+	}
+}
+
+// get train info and pacu info from heartbeat
+func getInfofromSTC(info []byte) {
+	//dataCount := info[0]
+	//Get train info
+	value := binary.BigEndian.Uint16(info[2:4]) //info[2] +  info[3] 列车号数据
+	//value := 3
+	active.TrainNum = "TS" + strconv.Itoa(int(value))
+
+	lfshook.NewLogger().Logger.Infof("TrainNum: %s", active.TrainNum)
+
+	//Get ICP volume
+	icpBit8 := info[9] & 0xF
+	icpBit1 := (info[9] >> 4) & 0xF
+	icpVol1 := 0
+	icpVol8 := 0
+
+	if icpBit1 == 0xe {
+		icpVol1 = 100
+	} else {
+		icpVol1 = int(icpBit1) * 7
+	}
+
+	if icpBit8 == 0xe {
+		icpVol8 = 100
+	} else {
+		icpVol8 = int(icpBit8) * 7
+	}
+
+	active.DeviceEndpoint.ICPInfo[0].Volume = fmt.Sprintf("%d", icpVol1)
+	active.DeviceEndpoint.ICPInfo[0].ID = "1"
+	active.DeviceEndpoint.ICPInfo[1].Volume = fmt.Sprintf("%d", icpVol8)
+	active.DeviceEndpoint.ICPInfo[1].ID = "8"
+
+	//Get Pacu info
+	eidsStat := info[1] //PACU mute
+	pacuStat := info[4] //PACU status
+
+	pacuVolume := binary.BigEndian.Uint32(info[5:9]) //info[5]-info[8]
+
+	//lfshook.NewLogger().Logger.Infof("=====pacuVolume====info[5:9]=======%x", info[5:9])
+	//lfshook.NewLogger().Logger.Infof("=====pacuStatus====info[4]=======%x", info[4])
+	for i := 0; i < 8; i++ {
+		eidsBit := (eidsStat >> i) & 0x01 // 右移 i 位,再与 1 取最低位
+		//fmt.Printf("eidsBit bit %d = %d\n", i, eidsBit) // bit 0 是 LSB(最低位,最右)
+
+		//get 1 bit every time
+		pacuStatBit := (pacuStat >> i) & 0x01
+		//fmt.Printf("pacuStatBit bit %d = %d\n", i, pacuStatBit)
+
+		//get 4 bit every time
+		//var pacuVolBit byte
+		pacuVolBit := (pacuVolume >> (i * 4)) & 0x0000000F
+		//fmt.Printf("pacuVolBit bit %d = %x\n", i, pacuVolBit)
+
+		active.DeviceEndpoint.PacuInfo[i].ID = strconv.Itoa(i + 1)
+		if eidsBit == 1 {
+			active.DeviceEndpoint.PacuInfo[i].Mute = true
+		} else {
+			active.DeviceEndpoint.PacuInfo[i].Mute = false
 		}
+		if pacuStatBit == 0 {
+			active.DeviceEndpoint.PacuInfo[i].Status = "normal"
+		} else {
+			active.DeviceEndpoint.PacuInfo[i].Status = "abnormal"
+		}
+		if int(pacuVolBit) == 14 {
+			active.DeviceEndpoint.PacuInfo[i].Volume = "100"
+		} else {
+			active.DeviceEndpoint.PacuInfo[i].Volume = strconv.Itoa(int(pacuVolBit) * 7)
+		}
+		//lfshook.NewLogger().Logger.Infof("PACU INFO===ID:%s===Mute:%+v===Stat:%s===Vol:%s", active.DeviceEndpoint.PacuInfo[i].ID, active.DeviceEndpoint.PacuInfo[i].Mute, active.DeviceEndpoint.PacuInfo[i].Status, active.DeviceEndpoint.PacuInfo[i].Volume)
 	}
 }
 
@@ -54,194 +141,305 @@ func HandleStcCmd(ctx context.Context, conn net.Conn) {
 func processPacket(packet []byte) {
 
 	if len(packet) < 6 {
-		fmt.Println("Invalid packet length")
+		utils.LoggerDebug.Printf("Get data wrong length from STC ! Data:%x", packet)
 		return
 	}
 
 	//for recv data log debug
 	if packet[5] != 0x03 && packet[5] != 0x0c && packet[5] != 0x01 {
-		lfshook.NewLogger().Logger.Infof("Get data from STC ===============:%x", packet)
+		utils.LoggerDebug.Printf("Get data from STC:%x", packet)
 	}
 
 	//check if the cmd type is avtive
 	if packet[5] == 0x03 { // ACTIVE
-		Active([2]byte{packet[8], packet[9]})
+		Active([3]byte{packet[8], packet[9], packet[10]})
 		return
 	}
 
 	//check if Master role
 	if !active.Master {
-		lfshook.NewLogger().Logger.Infof("=========Not Master Role Ignore data=============")
+		if tagLog == 0 {
+			utils.LoggerDebug.Printf("Not Master Role , Ignore all data from STC !")
+			tagLog = 1
+		}
 		return
 	}
+	tagLog = 0
 
 	switch packet[5] {
 	case 0x01: //heartbeat
-		/*
-			//PAD-OCC异常情况处理
-			if priority.OCCAnswer == 1 {
-				//定时监测ICP queue 转到OCC queue, 避免异常情形下PAD进入ICP queue之后无法被接听
-				resCaller, err := action.QueueStatus("0300", "") // check ICP queue, get entries
-				if err != nil {
-					lfshook.NewLogger().Infof("==ICP=QueueStatus==%+v", err)
-					return
-				}
-				if resCaller.Calls != "0" {
-					sort.Slice(resCaller.Entrys, func(i, j int) bool {
-						return resCaller.Entrys[i].Position < resCaller.Entrys[j].Position
-					})
-
-					for _, caller := range resCaller.Entrys {
-						priority.ICPAnswer = 0
-						lfshook.NewLogger().Infof("====Redirect to 0301 entry:%s=Pos:%s==", caller.CallerIDNum, caller.Position)
-						//order by pos
-						action.RedirectInQueue(caller.CallerIDNum, "0301", "queues-occ", caller.CallerIDNum) // redirect All ICP-PAD redirect to OCC queue
-						time.Sleep(time.Microsecond * 200)                                                   //200 ms delay
-					}
-				}
+		return
 
-				//定时监测OCC  queue, 避免异常情况下PAD 在OCC queue里面不能自动转到OCC
-				res, err := action.QueueStatus("0301", "") // check OCC queue ,if empty PAD end
-				if err != nil {
-					lfshook.NewLogger().Infof("==OCC=QueueStatus==%+v", err)
-					return
-				}
-				if res.Calls != "0" { //OCC queue is not empty
-					//	HangupAllLocalChan()
-					lfshook.NewLogger().Infof("====Start OCC-PAD===next==%+v", res)
-					if active.ActivedCab == "1" && action.ExtenStatus("1411") == "Idle" { //check active and OCC status
-						time.Sleep(time.Second)
-						PADChan := ""
-						for _, chanEntry := range res.Entrys {
-							lfshook.NewLogger().Infof("====PAD answered by OCC1 pos:%s===chan:%s=", chanEntry.Position, chanEntry.Channel)
-							if chanEntry.Position == "1" {
-								PADChan = chanEntry.Channel
-								break
-							}
-						}
-
-						if PADChan != "" {
-							alstatus.AlarmStatus(strings.Split(strings.Split(res.Entrys[0].Channel, "/")[1], "-")[0], "connect")
-							go action.RedirectInQueue(PADChan, "1411", "pad-page-occ-icp", "1") //PAD Page(OCC+ICPs)
-
-							go action.Dial("0401", "0512", "pad-rule-pacus-occ", "ano1", "ano1", "1") // PACUs dial OCC1
-						} else {
-							lfshook.NewLogger().Infof("===OCC-QueueStatus==PADCchan NULL")
-						}
-						break
-					} else if active.ActivedCab == "8" && action.ExtenStatus("1481") == "Idle" {
-						time.Sleep(time.Second)
-						PADChan := ""
-						for _, chanEntry := range res.Entrys {
-							lfshook.NewLogger().Infof("====PAD answered by OCC1 pos:%s===chan:%s=", chanEntry.Position, chanEntry.Channel)
-							if chanEntry.Position == "1" {
-								PADChan = chanEntry.Channel
-								break
-							}
-						}
-
-						if PADChan != "" {
-							alstatus.AlarmStatus(strings.Split(strings.Split(res.Entrys[0].Channel, "/")[1], "-")[0], "connect")
-							go action.RedirectInQueue(PADChan, "1481", "pad-page-occ-icp", "8") //PAD Page(OCC+ICPs)
-
-							go action.Dial("0401", "0512", "pad-rule-pacus-occ", "ano8", "ano8", "8") // PACUs dial OCC1
-						} else {
-							lfshook.NewLogger().Infof("===OCC-QueueStatus==PADCchan NULL")
-						}
-						break
-					}
-				}
-			}
-		*/
-		break
 	case 0x02: // STN
 		if active.ActivedCab != "" {
+			//检查是否有任务正在创建
+			action.WaitTaskCreate("STN")
+
 			if priority.CheckPriority("STN") {
-				action.HangupRunningTask("STN") //STN interrupt other
+
+				runningTaskName := action.InterruptRunningTask("STN") //STN interrupt other
+				if runningTaskName != "" {
+					time.Sleep(time.Millisecond * 100) //wait endpoint release
+				}
 				StationAnn(packet)
 			} else {
 				alstatus.PaStatus("", "STN", "refuse")
 			}
 		}
+
+		time.Sleep(3 * time.Second)
+		if priority.TaskCreating == "STN" {
+			priority.TaskCreating = ""
+		}
+
 	case 0x05: // SPC
 		if active.ActivedCab != "" {
+			//检查是否有任务正在创建
+			action.WaitTaskCreate("SPC")
+
 			if priority.CheckPriority("SPC") {
-				action.HangupRunningTask("SPC") //SPC interrupt other
+
+				runningTaskName := action.InterruptRunningTask("SPC") //SPC interrupt other
+				if runningTaskName != "" {
+					time.Sleep(time.Millisecond * 100) //wait endpoint release
+				}
 				SpecialAnn(packet)
 			} else {
 				alstatus.PaStatus("", "SPC", "refuse")
 			}
 		}
+
+		time.Sleep(3 * time.Second)
+		if priority.TaskCreating == "SPC" {
+			priority.TaskCreating = ""
+		}
+
 	case 0x06: // EMG
+
 		if active.ActivedCab != "" {
+			//检查是否有任务正在创建
+			action.WaitTaskCreate("EMG")
+
 			if priority.CheckPriority("EMG") {
-				action.HangupRunningTask("EMG") //EMG interrupt other
+
+				runningTaskName := action.InterruptRunningTask("EMG") //EMG interrupt other
+				if runningTaskName != "" {
+					time.Sleep(time.Millisecond * 100) //wait endpoint release
+				}
 				EmgMsg(packet)
 			} else {
 				alstatus.PaStatus("", "EMG", "refuse")
 			}
 		}
+
+		time.Sleep(3 * time.Second)
+		if priority.TaskCreating == "EMG" {
+			priority.TaskCreating = ""
+		}
+
 	case 0x07: // STOP
 		AnnStop([4]byte{packet[8], packet[9], packet[10], packet[11]})
 
 	case 0x08: // DCS
+
 		if active.ActivedCab != "" {
+			//检查是否有任务正在创建
+			action.WaitTaskCreate("DCS")
+
 			if priority.CheckPriority("DCS") {
-				action.HangupRunningTask("DCS") //DCS interrupt other
+
+				runningTaskName := action.InterruptRunningTask("DCS") //DCS interrupt other
+				if runningTaskName != "" {
+					time.Sleep(time.Millisecond * 100) //wait endpoint release
+				}
 				DcsAnn(packet)
 			} else {
 				alstatus.PaStatus("", "DCS", "refuse")
 			}
 		}
+
+		time.Sleep(3 * time.Second)
+		if priority.TaskCreating == "DCS" {
+			priority.TaskCreating = ""
+		}
+
 	case 0x09: // SELF CHECK
+
 		if active.ActivedCab != "" {
+			//检查是否有任务正在创建
+			action.WaitTaskCreate("CHK")
+
 			if priority.CheckPriority("CHK") {
-				action.HangupRunningTask("CHK") //CHK interrupt other
+
+				runningTaskName := action.InterruptRunningTask("CHK") //CHK interrupt other
+				if runningTaskName != "" {
+					time.Sleep(time.Millisecond * 100) //wait endpoint release
+				}
 				SelfCheck(packet)
 			} else {
 				alstatus.PaStatus("", "CHK", "refuse")
 			}
 		}
+
+		time.Sleep(3 * time.Second)
+		if priority.TaskCreating == "CHK" {
+			priority.TaskCreating = ""
+		}
+
 	case 0x0a: // Tone-test
 		if active.ActivedCab != "" {
+			//检查是否有任务正在创建
+			action.WaitTaskCreate("VOL")
+
 			if priority.CheckPriority("VOL") {
-				action.HangupRunningTask("VOL") //VOL interrupt other
+
+				runningTaskName := action.InterruptRunningTask("VOL") //VOL interrupt other
+				if runningTaskName != "" {
+					time.Sleep(time.Millisecond * 100) //wait endpoint release
+				}
 				ToneTest(packet)
 			} else {
 				alstatus.PaStatus("", "VOL", "refuse")
 			}
 		}
+
+		time.Sleep(3 * time.Second)
+		if priority.TaskCreating == "VOL" {
+			priority.TaskCreating = ""
+		}
+
 	case 0x0e: //TMS answer PAD
+
+		handler := packet[8]
+		key := suppressKey("exten", handler)
+
+		//Drop other handler in 2 sec
+		//PACUs---call---->ICP1
+		//PAD---->Chanspy(WEq)-->ICP1;PAD--->Call---->ICP2
+		if handler == 0x01 {
+			if _, loaded := suppressedExts.LoadOrStore(key, struct{}{}); loaded {
+				utils.LoggerDebug.Printf("Suppressed duplicate ICP Alarm (handler=0x01) for PAD: within 2 seconds")
+				return
+			}
+
+			time.AfterFunc(2*time.Second, func() {
+				suppressedExts.Delete(key)
+				utils.LoggerDebug.Printf("Suppression released .")
+			})
+		}
+
+		//检查是否有任务正在创建
+		action.WaitTaskCreate("PAD-TMS")
+
 		if priority.CheckPriority("PAD-TMS") {
-			action.HangupRunningTask("PAD-TMS") //PAD-TMS interrupt other
+
+			//Before Answer PAD
+			if packet[8] == 0x01 {
+
+				runningTaskName := action.InterruptRunningTask("PAD-TMS") //PAD-TMS interrupt other
+				if runningTaskName != "" {
+					time.Sleep(time.Millisecond * 100) //wait endpoint release
+				}
+			}
 
 			AlarmHandleTMS(packet)
-			active.QueueTimer.Stop()
+
+			if active.QueueTimer != nil {
+				if active.QueueTimer.Stop() {
+					utils.LoggerDebug.Printf("Stop PAD timer true !")
+				} else {
+					utils.LoggerDebug.Printf("Stop PAD timer false !")
+				}
+			}
+
 		} else {
 			alstatus.PaStatus("", "PAD-TMS", "refuse")
 		}
 
+		time.Sleep(3 * time.Second)
+		if priority.TaskCreating == "PAD-TMS" {
+			priority.TaskCreating = ""
+		}
+
 	case 0x0b: // reset all PAD
 		AlarmHoldResetAll(packet[8]) // reset all pad
 
-	//case 0x0c: // recored config
-	//	RecordStorageConf(packet[8:]) // RCD setting
+	case 0x0c: // Set PAD timeout
+		//lfshook.NewLogger().Logger.Infof("==type 0x0c===Get data from STC ====%x", packet)
+		PadTimeOutSetting(packet[8:]) // timeout setting
+
+		getInfofromSTC(packet[8:])
 
 	case 0x0d: // ICP answer PAD
+
+		handler := packet[8]
+		key := suppressKey("exten", handler)
+
+		//Drop other handler in 2 sec
+		//PACUs---call---->ICP1
+		//PAD---->Chanspy(WEq)-->ICP1;PAD--->Call---->ICP2
+		if handler == 0x01 {
+			if _, loaded := suppressedExts.LoadOrStore(key, struct{}{}); loaded {
+				utils.LoggerDebug.Printf("Suppressed duplicate ICP Alarm (handler=0x01) for PAD: within 2 seconds")
+				return
+			}
+
+			time.AfterFunc(2*time.Second, func() {
+				suppressedExts.Delete(key)
+				utils.LoggerDebug.Printf("Suppression released for key: %s", key)
+			})
+		}
+
+		//检查是否有任务正在创建
+		action.WaitTaskCreate("PAD-ICP")
+
 		if priority.CheckPriority("PAD-ICP") {
 
-			action.HangupRunningTask("PAD-ICP") //PAD-ICP interrupt other
-			active.QueueTimer.Stop()
-			AlarmHandleICP(packet) //
+			//Before Answer PAD
+			if packet[8] == 0x01 {
+
+				runningTaskName := action.InterruptRunningTask("PAD-ICP") //PAD-ICP interrupt other
+				if runningTaskName != "" {
+					time.Sleep(time.Millisecond * 100) //wait endpoint release
+				}
+			}
+
+			if active.QueueTimer != nil {
+				utils.LoggerDebug.Printf("PAD Timeout timer != nil !")
+				if active.QueueTimer.Stop() {
+					utils.LoggerDebug.Printf("Stop PAD timer true !")
+				} else {
+					utils.LoggerDebug.Printf("Stop PAD timer false !")
+				}
+			}
+
+			AlarmHandleICP(packet)
 		} else {
 			alstatus.PaStatus("", "PAD-ICP", "refuse")
 		}
 
+		time.Sleep(3 * time.Second)
+		if priority.TaskCreating == "PAD-ICP" {
+			priority.TaskCreating = ""
+		}
+
+		//case 0xf1: //Set remote master
+
 		//default:
 		//fmt.Printf("Unknown command: %x\n", packet[5])
 	}
 }
 
+func PadTimeOutSetting(data []byte) {
+
+	Seconds := data[7]
+	if Seconds != 0 {
+		active.PADTimeout = int(Seconds)
+		//lfshook.NewLogger().Logger.Infof("=========Set PAD Timeout seconds to %d ! ============", active.PADTimeout)
+	}
+
+}
+
 // STN , 自动报站广播
 func StationAnn(data []byte) (err error) {
 
@@ -255,44 +453,121 @@ func StationAnn(data []byte) (err error) {
 	//set spc voice tag
 	priority.SpecialVoice = specialVoice
 
+	utils.LoggerDebug.Printf("Type:STN	FileName:%x	Count:%x	SpecialVoice:%+v	Interval:%+v", filename, cycleCount, specialVoice, delay)
+
 	action.PlaybackPacu(strconv.Quote(filename), int(cycleCount), int(delay), "STN")
 	return nil
 }
 
 // 激活信号
-func Active(data [2]byte) {
+func Active(data [3]byte) {
 
 	//var info model.Sysinfo
-	//active.Actived = true
 	Signal := int(data[0])
-	Master := int(data[1])
-	//lfshook.NewLogger().Logger.Infof("Active data : %x", Signal)
+	master := int(data[1])
+	TrainInfo := int(data[2])
+
+	lfshook.NewLogger().Logger.Infof("=====active:%x======cab=%s Master=%d=====data:%x======", Signal, active.CabNum, master, data)
+
+	//Set Master
+	if master == 1 && active.CabNum == "1" {
+
+		active.Master = true
+		if !utils.CheckAsterisk() {
+			lfshook.NewLogger().Infof("Check asterisk , if not running , run cmd service asterisk start !")
+			cmd := exec.Command("service", "asterisk", "start")
+			cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+			err := cmd.Run()
+			if err != nil {
+				lfshook.NewLogger().Infof("Failed to start asterisk: %v", err)
+				return
+			}
+		}
+	} else if master == 8 && active.CabNum == "8" {
+
+		active.Master = true
+		if !utils.CheckAsterisk() {
+			lfshook.NewLogger().Infof("Check asterisk , if not running , run cmd service asterisk start !")
+			cmd := exec.Command("service", "asterisk", "start")
+			cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+			err := cmd.Run()
+			if err != nil {
+				lfshook.NewLogger().Infof("Failed to start asterisk: %v", err)
+				return
+			}
+		}
+	} else if master == 8 && active.CabNum == "1" {
+
+		if utils.CheckAsterisk() {
+			lfshook.NewLogger().Infof("Check asterisk , if running slave, run cmd service asterisk stop !")
+			cmd := exec.Command("service", "asterisk", "stop")
+			cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+			err := cmd.Run()
+			if err != nil {
+				lfshook.NewLogger().Infof("Failed to stop asterisk: %v", err)
+				return
+			}
+		}
+	} else if master == 1 && active.CabNum == "8" {
+		active.Master = false
+	}
 
-	if !active.Master && active.CabNum == "8" { //slave role , check the Master data from STC
-		if Master == 8 {
-			active.Master = true
+	//Set train info
+	if TrainInfo != 0 {
+		//Get train devide info
+		DevideInfo := TrainInfo & 0x20
+		if DevideInfo == 0x20 {
+			active.TrainDevide = 1
+
+			active.Master = true //列车断开,设置两边都Master
+			if !utils.CheckAsterisk() {
+				lfshook.NewLogger().Infof("Check asterisk , if not running , run cmd service asterisk start !")
+				cmd := exec.Command("service", "asterisk", "start")
+				cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+				err := cmd.Run()
+				if err != nil {
+					lfshook.NewLogger().Infof("Failed to start asterisk: %v", err)
+					return
+				}
+			}
+		} else {
+			active.TrainDevide = 0
 		}
-	} else if !active.Master && active.CabNum == "1" {
-		if Master == 1 {
-			active.Master = true
+
+		//Radio fault
+		RadioFault1 := TrainInfo & 0x03
+		RadioFault8 := TrainInfo & 0x0c
+		if RadioFault1 == 0 && RadioFault8 == 1 {
+			active.RadioFault = 1
+		} else {
+			active.RadioFault = 0
 		}
+		utils.LoggerDebug.Printf("RadioFault1:%x 	RadioFault1:%x		DevideInfo:%x", RadioFault1, RadioFault8, DevideInfo)
+
 	}
 
 	switch Signal {
 	case 0:
 
-		//lfshook.NewLogger().Logger.Infof("=================Inactive==================")
-		active.ActivedCab = ""
-		action.InActiveHangup()
+		if active.ActivedCab != "" {
+			active.ActivedCab = ""
+			action.InActiveHangup()
+		}
 
 	case 1:
-		active.ActivedCab = "1"
-		//lfshook.NewLogger().Logger.Infof("=================active===MC1===============")
+		//active signal from 8 to 1
+		if active.ActivedCab == "8" || active.ActivedCab == "" {
+			active.ActivedCab = "1"
+			action.InActiveHangup()
+		}
 
 	case 8:
+		//active signal from 1 to 8
+		if active.ActivedCab == "1" || active.ActivedCab == "" {
+			active.ActivedCab = "8"
+			action.InActiveHangup()
+		}
 
-		active.ActivedCab = "8"
-		//lfshook.NewLogger().Logger.Infof("=================active===MC8===============")
 	}
 }
 
@@ -305,6 +580,7 @@ func SpecialAnn(data []byte) {
 
 	filename := msgdata.SubstrByRune(string(data[11:]), 0, datalen-4)
 
+	utils.LoggerDebug.Printf("Type:SPC	FileName:%x	Count:%x	Interval:%+v", filename, cycleCount, delay)
 	if int(cycleCount) == 255 {
 		action.PlaybackPacu(strconv.Quote(filename), 9999999, int(delay), "SPC")
 	} else {
@@ -320,9 +596,9 @@ func EmgMsg(data []byte) {
 
 	filename := msgdata.SubstrByRune(string(data[11:]), 0, datalen-4)
 
+	utils.LoggerDebug.Printf("Type:EMG	FileName:%x	Count:%x	Interval:%+v", filename, cycleCount, delay)
 	if int(cycleCount) == 255 {
 		action.PlaybackPacu(strconv.Quote(filename), 9999999, int(delay), "EMG")
-		priority.ResumeEmgPara = priority.BroadcastResumeParas{FileName: filename, Count: 99999999, Delay: int(delay), BroadcastType: "EMG"}
 	} else {
 		action.PlaybackPacu(strconv.Quote(filename), int(cycleCount), int(delay), "EMG")
 	}
@@ -331,35 +607,37 @@ func EmgMsg(data []byte) {
 // 停止指定类型广播
 func AnnStop(data [4]byte) {
 
-	//PaType := ""
-	lfshook.NewLogger().Logger.Infof("=AnnStop Type  %x", data[0])
+	//lfshook.NewLogger().Logger.Infof("=========AnnStop Type  %x", data[0])
+	utils.LoggerDebug.Printf("Stop PA Type:%x (DCS=3,EMG=4,SPC=7,STN=8,SelfCheck=9,ToneTest=10)", data[0])
+
 	switch data[0] {
 	case 0x03:
-		if priority.RunningType == "DCS" {
-			action.HangupRunningTask("") //STOP DCS
-		}
+
+		action.HangupTask("DCS")           //STOP DCS
+		time.Sleep(time.Millisecond * 100) //wait endpoint release
 	case 0x04:
-		if priority.RunningType == "EMG" {
-			action.HangupRunningTask("AnnStop") //STOP EMG
-		}
+
+		action.HangupTask("EMG")           //STOP EMG
+		time.Sleep(time.Millisecond * 100) //wait endpoint release
 	case 0x07:
-		if priority.RunningType == "SPC" {
-			action.HangupRunningTask("AnnStop") //STOP SPC
-		}
+
+		action.HangupTask("SPC")           //STOP SPC
+		time.Sleep(time.Millisecond * 100) //wait endpoint release
 	case 0x08:
-		if priority.RunningType == "STN" {
-			action.HangupRunningTask("AnnStop") //STOP STN
-		}
+
+		action.HangupTask("STN")           //STOP STN
+		time.Sleep(time.Millisecond * 100) //wait endpoint release
 	case 0x09:
-		if priority.RunningType == "CHK" {
-			action.HangupRunningTask("AnnStop") //STOP CHK
-		}
+
+		action.HangupTask("CHK")           //STOP CHK
+		time.Sleep(time.Millisecond * 100) //wait endpoint release
 	case 0x0a:
-		if priority.RunningType == "VOL" {
-			action.HangupRunningTask("AnnStop") //STOP VOL
-		}
+
+		action.HangupTask("VOL")           //STOP VOL
+		time.Sleep(time.Millisecond * 100) //wait endpoint release
 	default:
-		action.HangupRunningTask("AnnStop")
+		action.InterruptRunningTask("")
+		time.Sleep(time.Millisecond * 100) //wait endpoint release
 	}
 }
 
@@ -371,6 +649,7 @@ func DcsAnn(data []byte) {
 
 	filename := msgdata.SubstrByRune(string(data[11:]), 0, datalen-4)
 
+	utils.LoggerDebug.Printf("Type:DCS	FileName:%x	Count:%x	Interval:%+v", filename, cycleCount, delay)
 	if int(cycleCount) == 255 {
 		action.PlaybackPacu(strconv.Quote(filename), 9999999, int(delay), "DCS")
 	} else {
@@ -388,6 +667,7 @@ func ToneTest(data []byte) {
 
 	filename := msgdata.SubstrByRune(string(data[12:]), 0, datalen-4)
 
+	utils.LoggerDebug.Printf("Type:ToneTest	FileName:%x	 Count:%x	Interval:%+v	Action:%x (0x01=start/0x02=stop)", filename, cycleCount, delay, check)
 	switch check {
 	case 0x01: //start
 		action.PlaybackPacu(strconv.Quote(filename), int(cycleCount), int(delay), "VOL")
@@ -402,20 +682,18 @@ func SelfCheck(data []byte) {
 
 	check := data[8]
 	delay := data[9]
-	//cycleCount := data[10]
-	cycleCount := 0x32
+	cycleCount := data[10]
+	//cycleCount := 0x32
 	datalen := int(data[11])
 
 	filename := msgdata.SubstrByRune(string(data[12:]), 0, datalen-4)
 
+	utils.LoggerDebug.Printf("Type:SelfCehck	FileName:%x	 Count:%x	Interval:%+v	Action:%x (0x01=start/0x02=stop)", filename, cycleCount, delay, check)
 	switch check {
 	case 0x01: //start
 
 		action.PlaybackPacu(strconv.Quote(filename), int(cycleCount), int(delay), "CHK")
 	case 0x02: //stop
-
-		//Pa status report
-		//priority.CleanPriorityTag()
 		action.HangupAllExcept("")
 	}
 }
@@ -438,32 +716,17 @@ func AlarmHandleICP(data []byte) {
 	pos := data[13]
 	exten := fmt.Sprintf("24%c%c", carr, pos)
 
-	key := suppressKey(exten, handler)
-
-	//Drop other handler in 2 sec
-	//PACUs---call---->ICP1
-	//PAD---->Chanspy(WEq)-->ICP1;PAD--->Call---->ICP2
-	if handler == 0x01 {
-		if _, loaded := suppressedExts.LoadOrStore(key, struct{}{}); loaded {
-			lfshook.NewLogger().Logger.Infof("Suppressed duplicate ICP Alarm (handler=0x01) for exten: %s within 4 seconds", exten)
-			return
-		}
-
-		time.AfterFunc(4*time.Second, func() {
-			suppressedExts.Delete(key)
-			lfshook.NewLogger().Logger.Debugf("Suppression released for key: %s", key)
-		})
-	}
-
+	utils.LoggerDebug.Printf("ICP Handle PAD:%s	Action:%x (answer=1,hold=2,hangup=3)", exten, handler)
 	switch handler {
 	case 0x01: //answer(ICP+Alarm+PACU)
 		//NotifyPaiu(exten, "answer")
 		priority.ICPAnswer = 1
 		if priority.PADStart == 0 {
-			alstatus.PaStatus(exten, "PAD", "start")
+			alstatus.PaStatus("", "PAD", "start")
 			priority.PADStart = 1
 		}
-		lfshook.NewLogger().Logger.Infof("================ICP Answer PAD================:%s ", exten)
+		utils.LoggerDebug.Printf("ICP Answer PAD:%s .", exten)
+
 		if active.ActivedCab == "1" {
 			action.Dial("0402", "0511", "pad-rule-pacus", "ani1", exten, "1") // PACUs dial ICP1
 			//goto ami event ConfbridgeJoin, ICP answer PAD
@@ -475,20 +738,24 @@ func AlarmHandleICP(data []byte) {
 		}
 
 	case 0x02: //hold  重新放回队列里面
+		utils.LoggerDebug.Printf("ICP Hold PAD-ICP PAD:%s !", exten)
 		active.NotifyPaiu(exten, "hold")
 		err := action.RedirectInQueue(exten, "0300", "queues-icp-redirect", "1")
 		if err != nil {
-			lfshook.NewLogger().Info(err)
+			utils.LoggerDebug.Printf("RedirectInQueue err:%+v", err)
 		}
-		action.HangupAllLocalChan()
+		action.InterruptRunningTask("")
+		time.Sleep(time.Millisecond * 100) //wait endpoint release
+		//action.HangupICP()
 
 	case 0x03: //hangup
 		//NotifyPaiu(exten, "hangup")
-		lfshook.NewLogger().Logger.Infof("=============STC== Hangup PAD=============== ")
+		utils.LoggerDebug.Printf("STC Hangup PAD-ICP !")
 		action.Hangup(exten) //Pad
-		action.HangupAllLocalChan()
-		action.HangupICP()
-		priority.CleanPriorityTag()
+		//action.HangupICP()
+
+		action.HangupTask("PAD-ICP")
+
 	}
 }
 
@@ -499,117 +766,60 @@ func AlarmHandleTMS(data []byte) {
 	carr := data[12]
 	pos := data[13]
 	exten := fmt.Sprintf("24%c%c", carr, pos)
-	PacuNum := fmt.Sprintf("21%c%c", carr, pos)
-
-	key := suppressKey(exten, handler)
-
-	//Drop other handler in 2 sec
-	// 只对 handler == 0x01 做 2 秒去重
-	if handler == 0x01 {
-		if _, loaded := suppressedExts.LoadOrStore(key, struct{}{}); loaded {
-			lfshook.NewLogger().Logger.Infof("Suppressed duplicate ICP Alarm (handler=0x01) for exten: %s within 4 seconds", exten)
-			return // 已存在,说明在2秒窗口期内,直接丢弃
-		}
+	PacuNum := fmt.Sprintf("21%c1", carr)
 
-		// 设置4秒后删除该 key,允许下次通过
-		time.AfterFunc(4*time.Second, func() {
-			suppressedExts.Delete(key)
-			lfshook.NewLogger().Logger.Debugf("Suppression released for key: %s", key)
-		})
-	}
+	utils.LoggerDebug.Printf("TMS Handle PAD:%s	PACU:%s	Action:%x (answer=1,hold=2,hangup=3)", exten, PacuNum, handler)
 
 	switch handler {
 	case 0x01: //answer(ICP+Alarm+PACU)
 		//PACU---call---->ICP1
 		//PAD---->Chanspy(WEq)-->ICP1;PAD--->Call---->ICP2
 		if priority.PADStart == 0 {
-			alstatus.PaStatus(exten, "PAD", "start")
+			alstatus.PaStatus("", "PAD", "start")
 			priority.PADStart = 1
 		}
 		priority.ICPAnswer = 1
-		lfshook.NewLogger().Logger.Infof("================TMS Answer PAD:%s===PACU:%s==========", exten, PacuNum)
+		utils.LoggerDebug.Printf("TMS Answer PAD:%s 	PACU:%s", exten, PacuNum)
+
 		if action.ExtenStatus(PacuNum) == "Idle" {
 			if active.ActivedCab == "1" {
-				action.Dial("0403", PacuNum, "default", PacuNum, exten, "1") // PACU dial ICP1
+				action.Dial("0403", PacuNum, "pad-tms-dial-pacu", PacuNum, exten, "1") // PACU dial ICP1
 				//goto ami event BridgeEnter, ICP8 whisper ICP1
 			} else if active.ActivedCab == "8" {
-				action.Dial("0403", PacuNum, "default", PacuNum, exten, "8") // PACU dial ICP8
+				action.Dial("0403", PacuNum, "pad-tms-dial-pacu", PacuNum, exten, "8") // PACU dial ICP8
 				//goto ami event BridgeEnter, ICP1 whisper ICP8
+			} else if active.ActivedCab == "" { // No cab occupied
+				action.Dial("0403", PacuNum, "pad-tms-dial-pacu", PacuNum, exten, "1") // PACU dial ICP1
 			}
 		} else {
 			action.RedirectInQueue(exten, "0405", "default", exten) // PAD dial ICPs
 		}
 
 	case 0x02: //hold  重新放回队列里面
+		utils.LoggerDebug.Printf("ICP Hold PAD-TMS PAD:%s !", exten)
 		active.NotifyPaiu(exten, "hold")
 		err := action.RedirectInQueue(exten, "0300", "queues-icp-redirect", "1")
 		if err != nil {
-			lfshook.NewLogger().Info(err)
+			utils.LoggerDebug.Printf("RedirectInQueue err:%+v", err)
 		}
 		action.HangupAllLocalChan()
 
 	case 0x03: //hangup
 		//NotifyPaiu(exten, "hangup")
+		utils.LoggerDebug.Printf("STC Hangup PAD-TMS !")
 		action.Hangup(exten) //Pad
-		action.HangupAllLocalChan()
-		action.HangupICP()
-		priority.CleanPriorityTag()
+		action.HangupTask("PAD-TMS")
+		//action.HangupTask("PAD-ICP")
 	}
 }
 
 // 挂断所有报警器
 func AlarmHoldResetAll(handler byte) {
-
-	//all hold
-
+	utils.LoggerDebug.Printf("Alarm Hold/Reset All !")
 	//hangup all actived PAD
 	action.HangupAllPAD()
 
 	//hangup running task
-	if priority.RunningType == "PAD-ICP" || priority.RunningType == "PAD-OCC" || priority.RunningType == "PAD-TMS" {
-		action.HangupRunningTask("AlarmHoldResetAll") //Reset PAD ALL
-		priority.CleanPriorityTag()
-	}
-}
-
-/*
-func RecordStorageConf(data []byte) {
-
-	var info model.RcdConf
-
-	info.PadRcdEnable = strconv.Itoa(int(data[0]))
-	info.PadRcdStorageDays = strconv.Itoa(int(data[1]))
-	info.PaRcdStorageDays = strconv.Itoa(int(data[2]))
-	info.CpaRcdStorageDays = strconv.Itoa(int(data[3]))
-
-	info.PadRcdDelDays = strconv.Itoa(int(data[4]))
-	info.PaRcdDelDays = strconv.Itoa(int(data[5]))
-	info.CpaRcdDelDays = strconv.Itoa(int(data[6]))
-	//info.OpaRcdStorageDays = int(data[7])
-	//info.OpaRcdDelDays = int(data[8])
-
-	//lfshook.NewLogger().Infof("=============Set record Conf : %+v", info)
-
-	filePath := "/etc/asterisk/recording.conf"
-	_, err := os.Stat(filePath)
-	if err != nil {
-		logrus.Error(err)
-		return
-	}
-	iniFile, err := ini.Load(filePath)
-	if err != nil {
-		logrus.Error(err)
-		return
-	}
-
-	iniFile.Section("general").Key("PADRCD").SetValue(info.PadRcdEnable)
-	iniFile.Section("general").Key("PADRCDDAYS").SetValue(info.PadRcdStorageDays)
-	iniFile.Section("general").Key("PARCDDAYS").SetValue(info.PaRcdStorageDays)
-	iniFile.Section("general").Key("CPARCDDAYS").SetValue(info.CpaRcdStorageDays)
-	iniFile.Section("general").Key("PADRCDDELDAYS").SetValue(info.PadRcdDelDays)
-	iniFile.Section("general").Key("PARCDDELDAYS").SetValue(info.PaRcdDelDays)
-	iniFile.Section("general").Key("CPARCDDELDAYS").SetValue(info.CpaRcdDelDays)
-	iniFile.SaveTo(filePath)
-
+	action.InterruptRunningTask("AlarmHoldResetAll") //Reset PAD ALL
+	time.Sleep(time.Millisecond * 100)               //wait endpoint release
 }
-*/

+ 47 - 16
internal/app/stc/data/msgdata.go

@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"encoding/binary"
 	"fmt"
+	"pbx-api-gin/pkg/utils"
 )
 
 // Protocol 定义协议数据结构
@@ -126,14 +127,15 @@ func SubstrByRune(s string, start, length int) string {
 	return string(runes[start:end])
 }
 
-// extractPacket 从 buffer 中提取第一个完整数据包
 func ExtractPacket(buf *bytes.Buffer) ([]byte, error) {
 	data := buf.Bytes()
-	startIdx := -1
-	endIdx := -1
+	leng := buf.Len()
+
+	//lfshook.NewLogger().Logger.Infof("========start=========ExtractPacket raw data len=%d, hex=%x", leng, data)
 
-	// 查找起始标记 0x7f 0x83 0x9d
-	for i := 0; i < len(data)-2; i++ {
+	// Step 1: 查找起始标记 0x7f 0x8e 0x9d
+	startIdx := -1
+	for i := 0; i <= len(data)-3; i++ { // <= len-3 防止越界
 		if data[i] == 0x7f && data[i+1] == 0x8e && data[i+2] == 0x9d {
 			startIdx = i
 			break
@@ -141,15 +143,41 @@ func ExtractPacket(buf *bytes.Buffer) ([]byte, error) {
 	}
 
 	if startIdx == -1 {
-		// 没找到起始标记,清掉前面无用数据(防止 OOM)
-		if buf.Len() > 1024 {
-			buf.Next(buf.Len() - 10) // 保留最后10字节继续查找
+		//无起始标记:保守策略——丢弃过长脏数据,保留最后 MAX_LOOKAHEAD 字节防漏包
+		const MAX_LOOKAHEAD = 32
+		if leng > MAX_LOOKAHEAD {
+			buf.Next(leng - MAX_LOOKAHEAD)
+			utils.LoggerDebug.Printf("No start marker found, trimmed buffer to last %d bytes .", MAX_LOOKAHEAD)
 		}
-		return nil, fmt.Errorf("no start marker found")
+		return nil, nil // 明确告知:需继续读取
+	}
+
+	// Step 2: 安全读取 DataLength(偏移 startIdx+6 ~ startIdx+8)
+	if startIdx+8 > len(data) {
+		utils.LoggerDebug.Printf("Data Error: startIdx=%d, need data[%d:%d] for DataLength but len=%d → insufficient data .",
+			startIdx, startIdx+6, startIdx+8, len(data))
+		return nil, nil // 数据不够,等下次读取
 	}
 
-	// 从起始位置开始查找结束标记 0xFE
-	for i := startIdx; i < len(data); i++ {
+	dataLen := binary.BigEndian.Uint16(data[startIdx+6 : startIdx+8])
+	//lfshook.NewLogger().Logger.Infof("ExtractPacket datalen=%d (0x%04x) at offset %d", dataLen, dataLen, startIdx+6)
+
+	// Step 3: 计算理论包结束位置:startIdx + 8(头长) + dataLen + 1(0xFE)
+	endPosTheo := startIdx + 8 + int(dataLen) + 1
+	if endPosTheo > len(data) {
+		utils.LoggerDebug.Printf("Data Error: theoretical endPos=%d > data len=%d → need more data .", endPosTheo, len(data))
+		return nil, nil // 包未收全,等下次读取
+	}
+	//lfshook.NewLogger().Logger.Infof("t=================end================%x", data[endPosTheo])
+	// Step 4: 在 [startIdx+8+dataLen, endPosTheo] 范围内查找 0xFE(容错:允许在理论位置附近 1~2 字节浮动)
+	searchStart := startIdx + 8 + int(dataLen)
+	searchEnd := endPosTheo
+	if searchEnd > len(data) {
+		searchEnd = len(data) // 安全截断
+	}
+
+	endIdx := -1
+	for i := searchStart; i <= searchEnd; i++ {
 		if data[i] == 0xFE {
 			endIdx = i
 			break
@@ -157,15 +185,18 @@ func ExtractPacket(buf *bytes.Buffer) ([]byte, error) {
 	}
 
 	if endIdx == -1 {
-		return nil, fmt.Errorf("no end marker found")
+		utils.LoggerDebug.Printf("Data Error: End tag 0xFE not found in expected range [%d,%d), dataLen=%d .", searchStart, searchEnd, dataLen)
+		return nil, nil // 结束符缺失,但可能是延迟到达,继续等
 	}
 
-	// 提取完整包 [startIdx 到 endIdx]
-	packet := make([]byte, endIdx-startIdx+1)
+	//找到完整包:[startIdx, endIdx](含两端)
+	packetLen := endIdx - startIdx + 1
+	packet := make([]byte, packetLen)
 	copy(packet, data[startIdx:endIdx+1])
 
-	// 从 buffer 中删除已处理的数据
+	// 从 buffer 中移除已处理部分
 	buf.Next(endIdx + 1)
-
+	//lfshook.NewLogger().Logger.Infof("extracted packet len=%d, start=0x7f8e9d, end=0xFE", packetLen)
+	//lfshook.NewLogger().Logger.Infof("=============return============== %x", packet)
 	return packet, nil
 }

+ 140 - 35
internal/app/stc/index.go

@@ -8,7 +8,6 @@ import (
 	"pbx-api-gin/internal/app/stc/broadcast"
 	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"
@@ -17,6 +16,7 @@ import (
 )
 
 func StartStcConnection(conn net.Conn, cab string) {
+	utils.LoggerDebug.Printf("Starting Connect to STC%s ...", cab)
 
 	var connMux sync.Mutex // 保护 conn 的读写
 	var conn1 net.Conn
@@ -28,13 +28,15 @@ func StartStcConnection(conn net.Conn, cab string) {
 		conn1, err = CreateConnection(cab)
 		if err != nil || conn1 == nil {
 			time.Sleep(2 * time.Second)
-			//lfshook.NewLogger().Logger.Infof("===========Reconnecting====To Cab:%s=======", cab)
+			utils.LoggerDebug.Printf("Reconnecting To Cab%s ......", cab)
 			continue
 		}
 
 		//set connection log
+		trainInfo := fmt.Sprintf("CabNumber %s", active.ActivedCab)
 		if logTag == 0 {
-			utils.Logger.Printf("Train Information: CabNumber %s, Message: Connection to Cab%s STC is up !", active.ActivedCab, cab)
+			utils.Logger.Printf("Train Information: %s, Message: Connection to Cab%s STC is up !", trainInfo, cab)
+			utils.LoggerDebug.Printf("Connection to Cab%s STC1 is up !", cab)
 			logTag = 1
 		}
 
@@ -81,7 +83,7 @@ func StartStcConnection(conn net.Conn, cab string) {
 
 		//set connection log
 		if logTag == 1 {
-			utils.Logger.Printf("Train Information: CabNumber %s, Message: Connection to Cab%s STC is down !", active.ActivedCab, cab)
+			utils.Logger.Printf("Train Information: %s, Message: Connection to Cab%s STC is down !", trainInfo, cab)
 			logTag = 0
 		}
 
@@ -109,7 +111,7 @@ func CreateConnection(RemoteCab string) (net.Conn, error) {
 				//lfshook.NewLogger().Logger.Infof("========Connect server err :%+v", err)
 				return nil, err
 			}
-			lfshook.NewLogger().Logger.Infof("Connect success :%s:%d", socket.RemoteAddr, socket.RemotePort)
+			utils.LoggerDebug.Printf("Connect success STC1:%s:%d from Cab1 !", socket.RemoteAddr, socket.RemotePort)
 			return conn, nil
 		} else { //in cab 8
 			dialer := &net.Dialer{
@@ -124,7 +126,7 @@ func CreateConnection(RemoteCab string) (net.Conn, error) {
 				//lfshook.NewLogger().Logger.Infof("========Connect server err :%+v", err)
 				return nil, err
 			}
-			lfshook.NewLogger().Logger.Infof("Connect success :%s:%d", socket.RemoteAddr, socket.RemotePort)
+			utils.LoggerDebug.Printf("Connect success STC1:%s:%d from Cab8 !", socket.RemoteAddr, socket.RemotePort)
 			return conn, nil
 		}
 	} else { // connect to MC8
@@ -143,7 +145,7 @@ func CreateConnection(RemoteCab string) (net.Conn, error) {
 				//lfshook.NewLogger().Logger.Infof("========Connect server err :%+v", err)
 				return nil, err
 			}
-			lfshook.NewLogger().Logger.Infof("Connect success MC8:%s:%d", socket.RemoteAddr8, socket.RemotePort)
+			utils.LoggerDebug.Printf("Connect success STC8:%s:%d from Cab1 !", socket.RemoteAddr8, socket.RemotePort)
 			return conn, nil
 		} else { //in cab 8
 			dialer := &net.Dialer{
@@ -158,7 +160,7 @@ func CreateConnection(RemoteCab string) (net.Conn, error) {
 				//lfshook.NewLogger().Logger.Infof("========Connect server err :%+v", err)
 				return nil, err
 			}
-			lfshook.NewLogger().Logger.Infof("Connect success MC8:%s:%d", socket.RemoteAddr8, socket.RemotePort)
+			utils.LoggerDebug.Printf("Connect success STC8:%s:%d from Cab8 !", socket.RemoteAddr8, socket.RemotePort)
 			return conn, nil
 		}
 	}
@@ -167,6 +169,7 @@ func CreateConnection(RemoteCab string) (net.Conn, error) {
 /*
 func StartConnectionToSipServer(conn net.Conn) {
 
+	lfshook.NewLogger().Infof("Connect to Slave Sip Server ... ")
 	var connMux sync.Mutex // 保护 conn 的读写
 	var conn1 net.Conn
 	var err error
@@ -176,14 +179,14 @@ func StartConnectionToSipServer(conn net.Conn) {
 		conn1, err = CreateConnectionSipServer()
 		if err != nil || conn1 == nil {
 			time.Sleep(2 * time.Second)
-			lfshook.NewLogger().Logger.Infof("===========Reconnecting==Sip Server=======")
+			//lfshook.NewLogger().Logger.Infof("===========Reconnecting==Sip Server=======")
 			continue
 		}
 
 		connMux.Lock()
 		oldConn := conn
 
-		socket.ConnToMaster = conn1
+		socket.ConnToSlave = conn1
 
 		connMux.Unlock()
 
@@ -201,7 +204,7 @@ func StartConnectionToSipServer(conn net.Conn) {
 			defer func() {
 				cancel()
 			}()
-			SendheartbeatToSipServer(ctx, conn1) // 改造 Sendheartbeat 接收 ctx
+			SendToRemoteMaster(ctx, conn1) // 改造 Sendheartbeat 接收 ctx
 		}()
 
 		// 等待连接断开(监听连接状态)
@@ -213,32 +216,27 @@ func StartConnectionToSipServer(conn net.Conn) {
 
 		lfshook.NewLogger().Logger.Info("Reconnecting in 1 second...")
 		time.Sleep(time.Second) // 重连前等待
-
-		//check connected Master tag; connection err change to Master role ,exit
-		//if socket.ConnectedMaster {
-		//	active.Master = true
-		//	return
-		//}
 	}
 }
 */
+/*
 // 连接Master sipserver
 func CreateConnectionSipServer() (net.Conn, error) {
 	dialer := &net.Dialer{
-		LocalAddr: &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: socket.LocalPort8}, // 固定本地端口
+		LocalAddr: &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: socket.LocalServerPort}, // 固定本地端口
 		Control:   controlTCPConn,
 		Timeout:   5 * time.Second,
 	}
 
-	DialAddr := fmt.Sprintf("%s:%d", socket.RemoteAddr, socket.LocalPort) // Connect to Cab1 Sip server
+	DialAddr := fmt.Sprintf("%s:%d", socket.RemoteAddr8, socket.LocalServerPort) // Connect to Cab8 Sip server
 	conn, err := dialer.Dial("tcp", DialAddr)
 	if err != nil {
-		//lfshook.NewLogger().Logger.Infof("========Connect server err :%+v", err)
+		//lfshook.NewLogger().Logger.Infof("========Connect SIP server err :%+v", err)
 		return nil, err
 	}
 	lfshook.NewLogger().Logger.Infof("Connect SIP Server success :%s:%d", socket.RemoteAddr, socket.LocalPort)
 	return conn, nil
-}
+}*/
 
 func controlTCPConn(network, address string, c syscall.RawConn) error {
 	return c.Control(func(fd uintptr) {
@@ -255,6 +253,7 @@ func Sendheartbeat(ctx context.Context, conn net.Conn) {
 	protocol.DataLength = 0x04
 	protocol.Data = make([]byte, 4)
 
+	utils.LoggerDebug.Printf("Start Sendheartbeat to STC .")
 	// 初始化协议...
 	ticker := time.NewTicker(2 * time.Second)
 	defer ticker.Stop()
@@ -287,13 +286,84 @@ func Sendheartbeat(ctx context.Context, conn net.Conn) {
 	}
 }
 
-func SendheartbeatToSipServer(ctx context.Context, conn net.Conn) {
-	var count uint8
+/*
+// cab == 8
+func RecvFromSipServer() {
+
+	lfshook.NewLogger().Infof("Connect to Master Sip Server  .... ")
+
+	listener, err := net.Listen("tcp", "0.0.0.0:10000")
+	if err != nil {
+		lfshook.NewLogger().Logger.Infof("Sever Listen cab1 err:%+v", err)
+	}
+	defer listener.Close()
+
+	for {
+		conn, err := listener.Accept() //blocked wait connection
+		if err != nil {
+			lfshook.NewLogger().Logger.Infof("Sever accept cab1 err:%+v", err)
+			continue
+		}
+
+		// 启动 goroutine 处理每个连接(支持并发)
+		//go HandleConnection(conn)
+		//clientAddr := conn.RemoteAddr().String()
+
+		buf := make([]byte, 1024)
+		for {
+			n, err := conn.Read(buf)
+			if n > 0 {
+				//安全截取实际读到的字节
+				//data := buf[:n]
+
+				//Set master = true
+				//if data[8] == 0x01 && data[5] == 0xf1 {
+				//	active.Master = true
+				//return // set to master , stop recv
+				//} // else if data[8] == 0x00 && data[5] == 0xf1 {
+				//active.Master = false
+				//}
+				//lfshook.NewLogger().Logger.Infof("Client  received %d bytes: hex=%x", n, data)
+
+				//Respond to remote
+				//if active.Master == true {
+				if _, werr := conn.Write([]byte("1")); werr != nil {
+					lfshook.NewLogger().Logger.Infof("Failed to write 'ok' to client: %+v", werr)
+				}
+				//} else {
+				//	if _, werr := conn.Write([]byte("0")); werr != nil {
+				//lfshook.NewLogger().Logger.Infof("Failed to write 'ok' to client: %+v", werr)
+				//	}
+				//}
+
+			}
+
+			if err == io.EOF {
+				//lfshook.NewLogger().Logger.Infof("Client %s 连接正常关闭", clientAddr)
+				break
+			}
+			if err != nil {
+				// 忽略临时错误(如 timeout),但记录非临时错误
+				if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
+					continue // 可选:超时后继续读(需配合 SetReadDeadline)
+				}
+				//lfshook.NewLogger().Logger.Infof("addr:%s read error: %+v", clientAddr, err)
+				break
+			}
+		}
+		lfshook.NewLogger().Logger.Infof("Connection closed from cab1 set Master = true !")
+		active.Master = true
+		//return
+	}
+}
+*/
+/*
+func SendToRemoteMaster(ctx context.Context, conn net.Conn) {
 
 	protocol := msgdata.NewProtocol()
-	protocol.MessageID = 0x21
-	protocol.DataLength = 0x04
-	protocol.Data = make([]byte, 4)
+	protocol.MessageID = 0xf1 //check master data type
+	protocol.DataLength = 0x02
+	protocol.Data = make([]byte, 2)
 
 	// 初始化协议...
 	ticker := time.NewTicker(2 * time.Second)
@@ -302,32 +372,67 @@ func SendheartbeatToSipServer(ctx context.Context, conn net.Conn) {
 	for {
 		select {
 		case <-ctx.Done():
-			//lfshook.NewLogger().Logger.Infof("Sendheartbeat===ctx==ret======")
 			return
 
 		case <-ticker.C:
-			count++
-			protocol.Data[0] = count
+
+			//set master data
+			//if active.Master {
+			//protocol.Data[0] = 0 //set remote master false
+			//} else {
+			protocol.Data[0] = 1 //set remote master true
+			//}
+
 			// 编码并发送数据...
 			encoded, err := protocol.Encode()
 			if err != nil {
-				//fmt.Printf("encode err : %v\n", err)
+
 				return
 			}
+
+			conn.SetWriteDeadline(time.Now().Add(1000 * time.Millisecond)) //发送1秒超时
 			if conn != nil {
 				_, err = conn.Write(encoded)
 				if err != nil {
-					//fmt.Printf("Send hearbeat err: %v\n", err)
 					conn.Close()
+
+					active.Master = true
 					return // 触发重连
 				}
+			}
 
-				//Set connected Master tag
-				if !socket.ConnectedMaster {
-					socket.ConnectedMaster = true
+			conn.SetReadDeadline(time.Now().Add(1000 * time.Millisecond)) //接收1秒超时
+			buf := make([]byte, 64)                                       // 足够接收短响应
+			n, err := conn.Read(buf)
+			if err != nil {
+				if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
+					lfshook.NewLogger().Logger.Infof("Read timeout waiting for remote response — may be OK (no response expected?)")
+				} else if errors.Is(err, io.EOF) || strings.Contains(err.Error(), "broken pipe") {
+					lfshook.NewLogger().Logger.Infof("Remote closed connection during read: %+v", err)
+					conn.Close()
+					return
+				} else {
+					lfshook.NewLogger().Logger.Infof("Read from remote failed unexpectedly: %+v", err)
 				}
-				//lfshook.NewLogger().Logger.Infof("Sendheartbeat===send ======%x", encoded)
+				active.Master = true
+				continue // 不中断循环,继续下一轮
+			}
+
+			//respond ok
+			if n > 0 {
+				continue
+				//response := strings.TrimSpace(string(buf[:n]))
+				//lfshook.NewLogger().Logger.Infof("Remote responded: [%s]", response)
+				//可选:校验响应,例如
+				//if response == "1" {
+				//	active.Master = false
+
+				//	if utils.CheckAsterisk() { //check asterisk available then stop asterisk
+				//		utils.ExecCmd("/etc/init.d/asterisk", "stop", "PBX")
+				//	}
+				//}
 			}
 		}
 	}
 }
+*/

+ 114 - 101
internal/app/stc/priority/index.go

@@ -2,177 +2,190 @@ package priority
 
 import (
 	"os"
-	"pbx-api-gin/pkg/lfshook"
+	"pbx-api-gin/pkg/utils"
 	"strconv"
+	"sync"
 
-	"github.com/sirupsen/logrus"
 	"gopkg.in/ini.v1"
 )
 
 var ICPAnswer = 0
 var OCCAnswer = 0
 
-var RunningTypePriority = 0
-var RunningType = ""
-var RunningPATaskChan = ""
-
-var TmpRunningTypePriority = 0
-var TmpRunningType = ""
-
 var SpecialVoice = 0
 
 var PADStart = 0
+var PADTMSStart = 0
 var PADOccStart = 0
-var PAStart = 0
+
+var TaskCreating = ""
+
+var PACreating = 0
+var C2CCreating = 0
+var CPACreating = 0
 
 var InterruptedPad = ""
+var filePath = "/etc/asterisk/priority.conf"
+
+type TaskInfo struct {
+	RunChannel   string `json:"runChannel"`
+	LocalChan    string `json:"localChan"`
+	RunType      string `json:"runType"`
+	Priority     int    `json:"priority"`
+	ConfbridgeID string `json:"confbridgeID"`
+	Running      bool
+}
 
-type BroadcastResumeParas struct {
-	FileName      string `json:"filename"`
-	Count         int    `json:"count"`
-	Delay         int    `json:"delay"`
-	BroadcastType string `json:"broadcastType"`
+type TaskPriority struct {
+	Priority int `json:"priority"`
 }
 
-var ResumeEmgPara BroadcastResumeParas
-
-type PriorityAll struct {
-	ManuPa string `json:"manualPa"`
-	CabCab string `json:"cabCab"`
-	PADICP string `json:"padIcp"`
-	PADTMS string `json:"padTms"`
-	PADOCC string `json:"padOcc"`
-	CPA    string `json:"cpa"`
-	EMG    string `json:"emg"`
-	SPC    string `json:"spc"`
-	DCS    string `json:"dcs"`
-	STN    string `json:"stn"`
-	CHK    string `json:"chk"`
-	VOL    string `json:"vol"`
+type Tasks struct {
+	PA     TaskPriority `json:"pa"`
+	C2C    TaskPriority `json:"c2c"`
+	PADICP TaskPriority `json:"padicp"`
+	PADTMS TaskPriority `json:"padtms"`
+	PADOCC TaskPriority `json:"padocc"`
+	CPA    TaskPriority `json:"cpa"`
+	EMG    TaskPriority `json:"emg"`
+	SPC    TaskPriority `json:"spc"`
+	DCS    TaskPriority `json:"dcs"`
+	STN    TaskPriority `json:"stn"`
+	CHK    TaskPriority `json:"chk"`
+	VOL    TaskPriority `json:"vol"`
 }
 
-var Priority PriorityAll
+var AllTasks Tasks
 
 func GetPriority() {
-	filePath := "/etc/asterisk/priority.conf"
+	utils.LoggerDebug.Printf("Init system priority !")
+
 	_, err := os.Stat(filePath)
 	if err != nil {
-		logrus.Error(err)
+		utils.LoggerDebug.Printf("GetPriority err :%+v", err)
 		return
 	}
 	iniFile, err := ini.Load(filePath)
 	if err != nil {
-		logrus.Error(err)
+		utils.LoggerDebug.Printf("GetPriority err :%+v", err)
 		return
 	}
 
-	Priority.CHK = iniFile.Section("general").Key("CHK").Value()
-	Priority.STN = iniFile.Section("general").Key("STN").Value()
-	Priority.DCS = iniFile.Section("general").Key("DCS").Value()
-	Priority.SPC = iniFile.Section("general").Key("SPC").Value()
-	Priority.EMG = iniFile.Section("general").Key("EMG").Value()
-	Priority.CPA = iniFile.Section("general").Key("CPA").Value()
-	Priority.PADOCC = iniFile.Section("general").Key("PAD-OCC").Value()
-	Priority.PADTMS = iniFile.Section("general").Key("PAD-TMS").Value()
-	Priority.PADICP = iniFile.Section("general").Key("PAD-ICP").Value()
-	Priority.CabCab = iniFile.Section("general").Key("CabCab").Value()
-	Priority.ManuPa = iniFile.Section("general").Key("ManuPa").Value()
-	Priority.VOL = iniFile.Section("general").Key("VOL").Value()
+	AllTasks.CHK.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("CHK").Value())
+	AllTasks.STN.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("STN").Value())
+	AllTasks.DCS.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("DCS").Value())
+	AllTasks.SPC.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("SPC").Value())
+	AllTasks.EMG.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("EMG").Value())
+	AllTasks.CPA.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("CPA").Value())
+	AllTasks.PADOCC.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("PAD-OCC").Value())
+	AllTasks.PADTMS.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("PAD-TMS").Value())
+	AllTasks.PADICP.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("PAD-ICP").Value())
+	AllTasks.C2C.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("CabCab").Value())
+	AllTasks.PA.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("ManuPa").Value())
+	AllTasks.VOL.Priority, _ = strconv.Atoi(iniFile.Section("general").Key("VOL").Value())
+
 }
 
-func GetPriorityByKey(key string) string {
+func GetPriorityByKey(key string) int {
 
 	switch key {
 	case "VOL":
-		return Priority.VOL
+		return AllTasks.VOL.Priority
 	case "CHK":
-		return Priority.CHK
+		return AllTasks.CHK.Priority
 	case "STN":
-		return Priority.STN
+		return AllTasks.STN.Priority
 	case "DCS":
-		return Priority.DCS
+		return AllTasks.DCS.Priority
 	case "SPC":
-		return Priority.SPC
+		return AllTasks.SPC.Priority
 	case "EMG":
-		return Priority.EMG
+		return AllTasks.EMG.Priority
 	case "CPA":
-		return Priority.CPA
+		return AllTasks.CPA.Priority
 	case "PAD-OCC":
-		return Priority.PADOCC
+		return AllTasks.PADOCC.Priority
 	case "PAD-TMS":
-		return Priority.PADTMS
+		return AllTasks.PADTMS.Priority
 	case "PAD-ICP":
-		return Priority.PADICP
+		return AllTasks.PADICP.Priority
 	case "CabCab":
-		return Priority.CabCab
+		return AllTasks.C2C.Priority
 	case "ManuPa":
-		return Priority.ManuPa
+		return AllTasks.PA.Priority
 	default:
-		return ""
+		return 0
 	}
 }
 
+var checkMutex sync.Mutex
+
 // check priority , if the running priority is lowwer than the to run priority
 func CheckPriority(runType string) bool {
-	//return true
-	lfshook.NewLogger().Logger.Infof("=========Check Pri runType:%s====RunningTypePriority:%d====SpecialVoice:%d========", runType, RunningTypePriority, SpecialVoice)
+	utils.LoggerDebug.Printf("CheckPriority  TorunType:%s 	SpecialVoice:%d", runType, SpecialVoice)
+	checkMutex.Lock()
+	defer checkMutex.Unlock()
+
 	//Check special voice can not interrupt
 	if SpecialVoice == 1 {
 		return false
 	}
 
-	//No any runing task now
-	if RunningTypePriority == 0 {
-		return true
-	}
-
 	//Get the to run priority number in the config file
-	ret, err := strconv.Atoi(GetPriorityByKey(runType))
-	if err != nil {
-		lfshook.NewLogger().Logger.Infof("=========Check Pri GetPriorityByKey err:%+v", err)
-		return false
-	}
+	toRunpriority := GetPriorityByKey(runType)
 
-	//special same priority number task return true
-	if ret == RunningTypePriority {
-		switch runType {
-		case "PAD-ICP":
+	runingtaskName, taskRuning, ok := RegistryTask.HighestPriorityRunningTask()
+	if ok {
+		utils.LoggerDebug.Printf("CheckPriority  TorunType:%s 	SpecialVoice:%d 	toRunpriority:%d 	runingpriority:%d ", runType, SpecialVoice, toRunpriority, taskRuning.Priority)
+		//if the running task priority is lowwer
+		if toRunpriority < taskRuning.Priority && toRunpriority != 0 {
 			return true
-		case "PAD-TMS":
-			return true
-			/*	case "VOL":
+
+		} else if toRunpriority == taskRuning.Priority {
+
+			switch runType {
+			case "PAD-ICP":
+				return true
+			case "PAD-TMS":
+				return true
+			case "EMG":
+				return true
+			case "SPC":
+				return true
+			case "DCS":
+				if taskRuning.RunType == "STN" {
+					return false
+				} else {
+					return true
+				}
+
+			case "STN":
+				if taskRuning.RunType == "DCS" {
+					return false
+				} else {
 					return true
-				case "CHK":
-					return true*/
+				}
+			case "CHK":
+				return true
+			case "VOL":
+				return true
+			}
 		}
-	}
 
-	//if the running task priority is lowwer
-	if ret < RunningTypePriority && ret != 0 {
-		return true
-	} else {
-		//C2C run together with other task except ..
-		if RunningType == "C2C" {
+		if runingtaskName == "C2C" {
 			if runType != "PA" && runType != "PAD-ICP" && runType != "PAD-TMS" && runType != "C2C" {
 				return true
 			}
 		} else if runType == "C2C" {
-			if RunningType != "PA" && RunningType != "PAD-ICP" && RunningType != "PAD-TMS" && RunningType != "C2C" {
+			if runingtaskName != "PA" && runingtaskName != "PAD-ICP" && runingtaskName != "PAD-TMS" && runingtaskName != "C2C" {
 				return true
 			}
 		}
-	}
-	return false
-}
 
-// Clean all priority tag
-func CleanPriorityTag() {
-	lfshook.NewLogger().Logger.Infof("=========CleanPriorityTag===============")
+	} else {
+		//No any runing task now
+		return true
+	}
 
-	ICPAnswer = 0
-	OCCAnswer = 0
-	RunningTypePriority = 0
-	RunningType = ""
-	SpecialVoice = 0
-	RunningPATaskChan = ""
+	return false
 }

+ 128 - 0
internal/app/stc/priority/task.go

@@ -0,0 +1,128 @@
+package priority
+
+import (
+	"sync"
+)
+
+// 全局导出变量:所有导入 pro 包的代码均可直接使用 pro.Registry
+var RegistryTask = NewTaskRegistry()
+
+// TaskRegistry:线程安全的任务注册表,仅保存 Running==true 的任务
+type TaskRegistry struct {
+	mu sync.RWMutex
+	m  map[string]TaskInfo // key: task name (e.g., "PA", "C2C", "DTMF")
+}
+
+// NewTaskRegistry 创建新注册表
+func NewTaskRegistry() *TaskRegistry {
+	return &TaskRegistry{
+		m: make(map[string]TaskInfo),
+	}
+}
+
+// Register 启动并注册一个任务(仅当 Running == true 时才存入)
+func (r *TaskRegistry) Register(name string, t TaskInfo) {
+	if !t.Running {
+		return // 不注册未运行的任务
+	}
+	r.mu.Lock()
+	defer r.mu.Unlock()
+	r.m[name] = t
+}
+
+// StopAndUnregister 标记任务为停止,并立即从注册表中删除(推荐在任务结束时调用)
+func (r *TaskRegistry) StopAndUnregister(name string) {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+	delete(r.m, name)
+}
+
+// UpdateStatus 更新任务状态;若设为 false,则自动删除(满足你“任务结束即删除”的需求)
+func (r *TaskRegistry) UpdateStatus(name string, newStatus bool) {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	if !newStatus {
+		delete(r.m, name) // 自动清理:Running=false → 移除
+		return
+	}
+
+	// 若 newStatus == true,尝试更新或插入(需原 task info 完整)
+	if t, ok := r.m[name]; ok {
+		t.Running = true
+		r.m[name] = t
+	} else {
+		// 如果没有旧记录,但你要“重新启用”,需外部提供完整 TaskInfo → 建议用 Register()
+	}
+}
+
+// Get 获取指定任务(nil if not found or not running)
+func (r *TaskRegistry) Get(name string) (TaskInfo, bool) {
+	r.mu.RLock()
+	defer r.mu.RUnlock()
+	t, ok := r.m[name]
+	return t, ok && t.Running
+}
+
+// ListAll 返回所有当前运行中的任务副本(安全遍历)
+func (r *TaskRegistry) ListAll() []TaskInfo {
+	r.mu.RLock()
+	defer r.mu.RUnlock()
+	list := make([]TaskInfo, 0, len(r.m))
+	for _, t := range r.m {
+		if t.Running { // 冗余检查,确保一致性
+			list = append(list, t)
+		}
+	}
+	return list
+}
+
+// HighestPriorityRunningTask returns the task with smallest Priority value,
+// which means highest scheduling priority (e.g., Priority=0 > 1 > 10).
+// Returns (key, task, found). If no task exists, found is false.
+func (r *TaskRegistry) HighestPriorityRunningTask() (string, TaskInfo, bool) {
+
+	r.mu.RLock()
+	defer r.mu.RUnlock()
+
+	var bestKey string
+	var best TaskInfo
+	found := false
+
+	for key, task := range r.m {
+		if !found || task.Priority < best.Priority {
+			bestKey = key
+			best = task
+			found = true
+		}
+	}
+	return bestKey, best, found
+}
+
+// HighestPriorityRunningTask1 returns the task with smallest Priority value except C2C,
+// which means highest scheduling priority (e.g., Priority=0 > 1 > 10).
+// Returns (key, task, found). If no task exists, found is false.
+func (r *TaskRegistry) HighestPriorityRunningTask1() (string, TaskInfo, bool) {
+
+	r.mu.RLock()
+	defer r.mu.RUnlock()
+
+	var bestKey string
+	var best TaskInfo
+	found := false
+
+	for key, task := range r.m {
+		if task.RunType == "C2C" {
+			continue
+		}
+
+		if !found || task.Priority < best.Priority {
+			bestKey = key
+			best = task
+			found = true
+		}
+	}
+
+	//lfshook.NewLogger().Infof("====HighestPriorityRunningTask1 ret=====%s", best.RunType)
+	return bestKey, best, found
+}

+ 8 - 10
internal/app/stc/sendstatus/status.go

@@ -6,7 +6,6 @@ import (
 	"pbx-api-gin/internal/app/stc/active"
 	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"
 )
 
@@ -14,10 +13,10 @@ func SendToStc(conn net.Conn, data []byte) {
 
 	_, err := conn.Write(data)
 	if err != nil {
-		fmt.Println("send msg err:", err)
+		utils.LoggerDebug.Printf("Send To STC msg err:%+v", err)
 		conn.Close()
 	}
-	lfshook.NewLogger().Logger.Infof("==send==data:==%x", data)
+	//lfshook.NewLogger().Logger.Infof("SendToStc data:%x", data)
 }
 
 // report alarm status to STC
@@ -78,7 +77,7 @@ func AlarmStatus(exten string, status string) {
 		return
 	}
 	//check if actived
-	lfshook.NewLogger().Logger.Infof("===AlarmStatus=ext:%s===carr:%x==========pos:%x=========status:%x", exten, protocol.Data[0], protocol.Data[1], protocol.Data[2])
+	utils.LoggerDebug.Printf("PAD number:%s		CarNum:%x 	 Pos:%x 	Status:%x", exten, protocol.Data[0], protocol.Data[1], protocol.Data[2])
 
 	if socket.Conn != nil {
 		SendToStc(socket.Conn, encoded)
@@ -97,7 +96,7 @@ func PaStatus(src string, patype string, operation string) {
 	if !active.Master {
 		return
 	}
-	lfshook.NewLogger().Logger.Infof("===PAStatus=Startext:%s=== type:%s=========action:%s", src, patype, operation)
+	utils.LoggerDebug.Printf("PA Status Src:%s 		Type:%s		Status:%s", src, patype, operation)
 	protocol := msgdata.NewProtocol()
 	protocol.MessageID = 0x22
 	protocol.DataLength = 0x04
@@ -157,7 +156,7 @@ func PaStatus(src string, patype string, operation string) {
 
 	encoded, errEn := protocol.Encode()
 	if errEn != nil {
-		fmt.Println("Encode error:", errEn)
+		utils.LoggerDebug.Printf("Encode error:%+v", errEn)
 		return
 	}
 
@@ -179,7 +178,7 @@ func OccPad(operation string) {
 		return
 	}
 
-	lfshook.NewLogger().Logger.Infof("===OCC-PAD========action:%s", operation)
+	utils.LoggerDebug.Printf("OCC-PAD status:%s", operation)
 	protocol := msgdata.NewProtocol()
 	protocol.MessageID = 0x2A
 	protocol.DataLength = 0x04
@@ -197,7 +196,7 @@ func OccPad(operation string) {
 
 	encoded, errEn := protocol.Encode()
 	if errEn != nil {
-		fmt.Println("Encode error:", errEn)
+		utils.LoggerDebug.Printf("Encode error:%+v", errEn)
 		return
 	}
 
@@ -251,7 +250,7 @@ func SendRecordFile(filename, rcdtype string) {
 	}
 	encoded, errEn := protocol.Encode()
 	if errEn != nil {
-		fmt.Println("Encode error:", errEn)
+		utils.LoggerDebug.Printf("Encode error:%+v", errEn)
 		return
 	}
 
@@ -262,5 +261,4 @@ func SendRecordFile(filename, rcdtype string) {
 	if socket.Conn8 != nil {
 		SendToStc(socket.Conn8, encoded)
 	}
-
 }

+ 63 - 14
internal/app/stc/socket/index.go

@@ -3,48 +3,97 @@ package socket
 import (
 	"fmt"
 	"net"
+	"pbx-api-gin/internal/app/stc/active"
+	"pbx-api-gin/pkg/utils"
+	"time"
 )
 
 var Conn net.Conn
 var Conn8 net.Conn
 
-var ConnToMaster net.Conn
+var ConnToSlave net.Conn
 
 const RemotePort = 10100
 const LocalPort = 10201
 const LocalPort8 = 10202
 
-//const RemoteAddr8 = "192.168.17.14"
+const LocalServerPort = 10000
 
 const RemoteAddr = "10.0.11.11"
 
 const RemoteAddr8 = "10.0.11.81"
 
-var ConnectedMaster bool
-
 func IsIPExists(targetIP string) (bool, error) {
 	// 解析目标 IP
+	utils.LoggerDebug.Printf("Checking device IP , Set CabNumber ......")
 	ip := net.ParseIP(targetIP)
 	if ip == nil {
 		return false, fmt.Errorf("invalid IP address: %s", targetIP)
 	}
 
-	// 获取所有网络接口的地址
-	addrs, err := net.InterfaceAddrs()
+	interfaces, err := net.Interfaces()
 	if err != nil {
 		return false, err
 	}
 
-	// 遍历每个接口地址
-	for _, addr := range addrs {
-		// 只处理 IP 网络类型地址 (*net.IPNet)
-		if ipnet, ok := addr.(*net.IPNet); ok {
-			// 检查该网段是否包含目标 IP
-			if ipnet.Contains(ip) {
-				return true, nil
+	for _, iface := range interfaces {
+		addrs, err := iface.Addrs()
+		if err != nil {
+			continue // 忽略无法读取地址的接口
+		}
+		for _, addr := range addrs {
+			if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP != nil {
+				// 精确匹配 IP(忽略掩码,只看地址本身是否一致)
+				if ipnet.IP.Equal(ip) ||
+					(ip.To4() != nil && ipnet.IP.To4() != nil && ipnet.IP.To4().Equal(ip.To4())) {
+					return true, nil
+				}
 			}
 		}
 	}
-
 	return false, nil
 }
+
+// Get eth0 IP
+func SetMasterCabNum() {
+
+	count := 0
+
+getIP:
+
+	utils.LoggerDebug.Printf("Checking system IP  ....")
+	ext1, err := IsIPExists("10.0.11.11")
+	if err != nil {
+		utils.LoggerDebug.Printf("Check IP 10.0.11.11 err :%+v", err)
+	}
+	// Init cab1 number and master role
+	if ext1 {
+		active.CabNum = "1"
+		active.Master = true
+		return
+	}
+
+	ext8, err := IsIPExists("10.0.11.81")
+	if err != nil {
+		utils.LoggerDebug.Printf("Check IP 10.0.11.81 err:%+v", err)
+	}
+	// Init cab8 number and master role
+	if ext8 {
+		active.CabNum = "8"
+		active.Master = false
+		return
+	}
+
+	if !(ext1 || ext8) {
+		if count < 5 {
+			count++
+			time.Sleep(time.Second * 2)
+			goto getIP
+		}
+		utils.LoggerDebug.Printf("Can not get device IP ! Set Cabnum=1  Master=true by default .")
+
+		active.CabNum = "1"
+		active.Master = true
+	}
+
+}

+ 3 - 28
internal/pkg/configs/decode.go

@@ -20,49 +20,24 @@ type Config struct {
 	AsteriskAMIUser   string `yaml:"asteriskAMIUser"`   // User
 	AsteriskAMISecret string `yaml:"asteriskAMISecret"` // Secret
 
-	//AsteriskAGIPort string `yaml:"asteriskAGIPort"` // AGIPort
-
-	//AsteriskTriggerPath      string `yaml:"asteriskTriggerPath"`      // 广播触发规则
-	//AsteriskPagingPath       string `yaml:"asteriskPagingPath"`       // 广播规则
-	//AsteriskBroadcastTimeout int64  `yaml:"asteriskBroadcastTimeout"` // 广播响铃时长
-	//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
-
-	//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
 	RecordEventLog string `yaml:"recordingEvent"` //recordingEventfile
+	DebugLogPath   string `yaml:"debugLog"`       //debugLog
 	ProcessRecord  string `yaml:"processRecord"`  //processRecord
 
-	//StoragePath string `yaml:"storagePath"` // 存储目录
-
-	//AllEventPushUrl string `yaml:"allEventPushUrl"`
-
-	//WebHost string `yaml:"webhost"` //Host
-	//WebPort int64  `yaml:"webport"` //port
-
-	//AllowOrigin string `yaml:"allowOrigin"` // allowOrigin
-	//AllowOrigin string `yaml:"allowOrigin"` // allowOrigin
-
 	LogLevel log.Level //logLevel
 }
 
 // ConfigPath 配置文件路径
-var ConfigPath = "/data/test/configs/config.yaml"
+var ConfigPath = "/data/PA/configs/config.yaml"
 
 // ConfigGlobal 全局配置变量
 var ConfigGlobal *Config
 
 // DecodeConfig 解析配置
 func DecodeConfig() {
+
 	_, err := os.Stat(ConfigPath)
 	if err != nil {
 		lfshook.NewLogger().Errorf("config file not exist %+v", err)

+ 3 - 3
pkg/utils/cmd.go

@@ -8,7 +8,7 @@ import (
 	"time"
 )
 
-//ExecCmdAsync 执行指定命令
+// ExecCmdAsync 执行指定命令
 func ExecCmdAsync(cmdName string, arg ...string) (stdOut, errOut string, err error) {
 	cmd := exec.Command(cmdName, arg...)
 	var stdout, stderr bytes.Buffer
@@ -28,7 +28,7 @@ func ExecCmdAsync(cmdName string, arg ...string) (stdOut, errOut string, err err
 	return outStr, errStr, err
 }
 
-//ExecCmd 执行指定命令
+// ExecCmd 执行指定命令
 func ExecCmd(cmdName string, arg ...string) (stdOut, errOut string, err error) {
 	cmd := exec.Command(cmdName, arg...)
 	var stdout, stderr bytes.Buffer
@@ -49,7 +49,7 @@ func ExecCmd(cmdName string, arg ...string) (stdOut, errOut string, err error) {
 		if err != nil {
 			lfshook.NewLogger().Errorf("cmd.Wait(%s) %s\n", cmdName, err)
 		} else {
-			lfshook.NewLogger().Info("cmd.Wait(%s)\n", cmdName)
+			lfshook.NewLogger().Infof("cmd.Wait(%s)\n", cmdName)
 		}
 		cancel()
 	}()

+ 19 - 24
pkg/utils/file.go

@@ -9,6 +9,7 @@ import (
 	"os/exec"
 	"path/filepath"
 	"pbx-api-gin/internal/pkg/configs"
+	"pbx-api-gin/pkg/lfshook"
 	"strconv"
 	"strings"
 )
@@ -33,6 +34,7 @@ func GetDuration(filePath string) (int, error) {
 
 	output, err := cmd.Output()
 	if err != nil {
+		lfshook.NewLogger().Logger.Infof("=======GetDuration err:%+v ! ============", output)
 		return 0, err
 	}
 
@@ -107,7 +109,8 @@ func ConvertAndSegmentWAV(inputFile string, outputPrefix string) ([]string, erro
 	output, err := cmd.CombinedOutput()
 	if err != nil && !strings.Contains(string(output), "Conversion failed!") {
 		// 即使有警告(如非关键错误),只要文件生成了就继续解析
-		log.Printf("ffmpeg 执行时有警告: %v", err)
+		//log.Printf("ffmpeg 执行时有警告: %v", err)
+		lfshook.NewLogger().Logger.Infof("=======ConvertAndSegmentWAV err:%+v ! ============", output)
 	}
 	// 解析输出,提取所有生成的文件名
 	files := extractFilenamesFromFFmpegOutput(outputPrefix)
@@ -121,32 +124,26 @@ func extractFilenamesFromFFmpegOutput(prefix string) []string {
 	pattern := fmt.Sprintf("%s_*", prefix)
 	matches, err := filepath.Glob(pattern)
 	if err != nil {
-		log.Fatal("模式匹配错误:", err)
+		//log.Fatal("模式匹配错误:", err)
+		lfshook.NewLogger().Logger.Infof("=======模式匹配错误:%+v ! ============", 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) // 自动带时间戳
+var LoggerDebug *log.Logger
+
+func InitLog() {
+	lfshook.NewLogger().Logger.Infof("=======模式匹配错误:%+v ! ============", configs.ConfigGlobal)
+	file, _ := os.OpenFile(configs.ConfigGlobal.RecordEventLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
+	//Logger = log.New(file, "", log.LstdFlags) // 自动带时间戳
+	Logger = log.New(file, "", log.LstdFlags|log.Lmicroseconds)
+	//lfshook.NewLogger().Printf("software version: V10.01")
+
+	fileDebug, _ := os.OpenFile(configs.ConfigGlobal.DebugLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
+	//Logger = log.New(file, "", log.LstdFlags) // 自动带时间戳
+	LoggerDebug = log.New(fileDebug, "", log.LstdFlags|log.Lmicroseconds|log.Llongfile)
 }
 
 func GetPadInfo(base string) (padType, padNum, connectedCab string) {
@@ -155,12 +152,10 @@ func GetPadInfo(base string) (padType, padNum, connectedCab string) {
 
 	// 取最后一段
 	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]
+	return ret[0], ret[1], ret[2]
 }

+ 51 - 0
pkg/utils/utils.go

@@ -4,7 +4,9 @@ import (
 	"flag"
 	"fmt"
 	"math/rand"
+	"net"
 	"os"
+	"pbx-api-gin/pkg/lfshook"
 	"strings"
 	"time"
 )
@@ -172,3 +174,52 @@ func IndexOf(sliceID []string, value string) int {
 	}
 	return -1
 }
+
+func CheckAsterisk() bool {
+	target := "127.0.0.1:5060"
+	count := 2
+udpDial:
+	conn, err := net.DialTimeout("udp", target, 1*time.Second)
+	if err != nil {
+		return false
+	}
+	defer conn.Close()
+
+	// 构造最小 SIP OPTIONS 请求(RFC 3261)
+	req := "OPTIONS sip:test@" + target + " SIP/2.0\r\n" +
+		"Via: SIP/2.0/UDP 127.0.0.1:5061;branch=z9hG4bK123456\r\n" +
+		"Max-Forwards: 70\r\n" +
+		"To: <sip:test@" + target + ">\r\n" +
+		"From: <sip:probe@127.0.0.1>;tag=abc123\r\n" +
+		"Call-ID: 123456789@127.0.0.1\r\n" +
+		"CSeq: 1 OPTIONS\r\n" +
+		"Contact: <sip:probe@127.0.0.1:5061>\r\n" +
+		"Accept: application/sdp\r\n" +
+		"Content-Length: 0\r\n\r\n"
+
+	_, err = conn.Write([]byte(req))
+	if err != nil {
+		time.Sleep(time.Second)
+		_, err = conn.Write([]byte(req))
+		if err != nil {
+			return false
+		}
+	}
+
+	time.Sleep(time.Millisecond * 500)
+	//conn.SetReadDeadline(time.Now().Add(1 * time.Second))
+	buf := make([]byte, 2048)
+	n, err := conn.Read(buf)
+	if err != nil || n == 0 {
+		count--
+		lfshook.NewLogger().Logger.Infof("=====CheckAsterisk response==err==count:%d ", count)
+		if count > 0 {
+			time.Sleep(time.Millisecond * 500)
+			goto udpDial
+		}
+		return false
+	}
+	resp := string(buf[:n])
+
+	return strings.HasPrefix(resp, "SIP/2.0")
+}

+ 39 - 0
test.go

@@ -0,0 +1,39 @@
+package test
+
+import "fmt"
+
+func main1() {
+	// 构建映射表:map[列车号][位置] = 车厢号
+	trainMap := make(map[string]map[int]string)
+	// 初始化所有列车号
+	trains := []string{"TS1", "TS2", "TS3", "TS4", "TS5", "TS6", "TS7", "TS8", "TS9", "TS10", "TS11", "TS12", "TS13"}
+	//positions := []string{"1", "2", "3", "4", "5", "6", "7", "8"}
+	// 数据:从你的表格中提取
+	data := [][]string{
+		{"D433", "P433", "M433", "K433", "C434", "M434", "P434", "D434"},
+		{"D435", "P435", "M435", "K435", "C436", "M436", "P436", "D436"},
+		{"D437", "P437", "M437", "K437", "C438", "M438", "P438", "D438"},
+		{"D439", "P439", "M439", "K439", "C440", "M440", "P440", "D440"},
+		{"D441", "P441", "M441", "K441", "C442", "M442", "P442", "D442"},
+		{"D443", "P443", "M443", "K443", "C444", "M444", "P444", "D444"},
+		{"D445", "P445", "M445", "K445", "C446", "M446", "P446", "D446"},
+		{"D447", "P447", "M447", "K447", "C448", "M448", "P448", "D448"},
+		{"D449", "P449", "M449", "K449", "C450", "M450", "P450", "D450"},
+		{"D451", "P451", "M451", "K451", "C452", "M452", "P452", "D452"},
+		{"D453", "P653", "M453", "K453", "C454", "M454", "P454", "D454"},
+		{"D455", "P455", "M455", "K455", "C456", "M456", "P456", "D456"},
+		{"D457", "P457", "M457", "K457", "C458", "M458", "P458", "D458"},
+	}
+
+	// 填充 map
+	for i, train := range trains {
+		trainMap[train] = make(map[int]string)
+		for j, car := range data[i] {
+			trainMap[train][j+1] = car //位置从1开始
+		}
+	}
+	// 示例查询
+	fmt.Println(trainMap["TS1"][1])  // 输出: D433
+	fmt.Println(trainMap["TS5"][3])  // 输出: M441
+	fmt.Println(trainMap["TS13"][8]) // 输出: D458
+}