1
0

2 Commits c526469f71 ... d49c02ef5d

Autor SHA1 Nachricht Datum
  dujunchen d49c02ef5d PAD alarm finish vor 1 Woche
  dujunchen 869998ac75 update vor 1 Woche

+ 38 - 9
internal/app/ami/action/call.go

@@ -51,16 +51,16 @@ func ChanSpy(src, dst string, whisper, bargein bool) {
 	lfshook.NewLogger().Infof("chan spy src:%s dst:%s", src, dst)
 
 	channel := fmt.Sprintf("%s/%s", utils.DialPrefix, dst)
-	data := fmt.Sprintf("%s/%s,q,E", utils.DialPrefix, src)
-
-	if whisper {
-		data = fmt.Sprintf("%s,w", data)
-	}
-
-	if bargein {
-		data = fmt.Sprintf("%s,B", data)
-	}
+	data := fmt.Sprintf("%s/%s,qBE", utils.DialPrefix, src)
+	/*
+		if whisper {
+			data = fmt.Sprintf("%s,w", data)
+		}
 
+		if bargein {
+			data = fmt.Sprintf("%s,B", data)
+		}
+	*/
 	action := map[string]string{
 		"Action":      "Originate",
 		"Channel":     channel, // 不存在的通话
@@ -179,6 +179,35 @@ func Redirect(channel, dst, dialrule string) (err error) {
 	return err
 }
 
+// Redirect 转接
+func RedirectInQueue(channel, dst, dialrule, callerID string) (err error) {
+	//callerID := "redirect"
+	lfshook.NewLogger().Infof("redirect src %s to dst %s", channel, dst)
+	if !utils.IsChannel(channel) {
+		//callerID = channel
+		if channel, err = GetChannelByExtenNotBridged(channel); err != nil {
+			return err
+		}
+	}
+
+	action := map[string]string{
+		"Action":   "Redirect",
+		"Channel":  channel,
+		"Exten":    dst,
+		"Context":  dialrule,
+		"CallerID": callerID,
+		"Priority": "1",
+		"async":    "true",
+	}
+	lfshook.NewLogger().Infof("redirect %+v", action)
+	res, _, err := AminInstance.Send(action)
+	if err != nil {
+		lfshook.NewLogger().Error(err)
+	}
+	lfshook.NewLogger().Info(res)
+	return err
+}
+
 // Redirect 转接
 func BlindTransfer(channel, dst, dialrule string) (err error) {
 

+ 27 - 0
internal/app/ami/action/channel.go

@@ -65,6 +65,33 @@ func GetChannelByExten(exten string) (channel string, err error) {
 	return channel, nil
 }
 
+// GetChannelByExten  通过 exten 查询对应通道
+func GetChannelByExtenNotBridged(exten string) (channel string, err error) {
+	lfshook.NewLogger().Infof("GetChannelByExten %s", exten)
+	_, events, err := AminInstance.Send(map[string]string{
+		"Action": "CoreShowChannels",
+	})
+	if err != nil {
+		lfshook.NewLogger().Errorf("core show channels error %+v", err)
+		return "", err
+	}
+
+	for _, event := range events {
+		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)
+			break
+		}
+	}
+	if channel == "" {
+		lfshook.NewLogger().Errorf("not found channel %s", exten)
+		return "", errors.New("not found channel")
+	}
+
+	return channel, nil
+}
+
 // GetBridgedChan  通过 exten 查询对应通道
 func GetExtenChan(exten string) (channel string, err error) {
 	lfshook.NewLogger().Infof("GetExtenChan %s", exten)

+ 25 - 20
internal/app/ami/action/index.go

@@ -40,7 +40,8 @@ func HandleAMI(event map[string]string, conn net.Conn) {
 
 			alstatus.AlarmStatus(event["CallerIDNum"], "dial", conn)
 		}
-	case "DialEnd":
+	case "1111":
+		//case "DialEnd":
 		lfshook.NewLogger().Infof("=========%s", event["Event"])
 		if utils.IsPAIU(event["CallerIDNum"]) {
 			alstatus.AlarmStatus(event["CallerIDNum"], "Idle", conn) // Alarm dial end
@@ -50,24 +51,6 @@ func HandleAMI(event map[string]string, conn net.Conn) {
 		if utils.IsPAIU(event["CallerIDNum"]) { // Alarm join the queue
 			alstatus.AlarmStatus(event["CallerIDNum"], "queue", conn)
 
-			switch string(event["CallerIDNum"][2]) { // connect the pacu to pad acording to the pad number
-			case "1":
-				ChanSpy(event["CallerIDNum"], pacu1, false, true) //connect pacu to pad
-			case "2":
-				ChanSpy(event["CallerIDNum"], pacu2, false, true) //connect pacu to pad
-			case "3":
-				ChanSpy(event["CallerIDNum"], pacu3, false, true) //connect pacu to pad
-			case "4":
-				ChanSpy(event["CallerIDNum"], pacu4, false, true) //connect pacu to pad
-			case "5":
-				ChanSpy(event["CallerIDNum"], pacu5, false, true) //connect pacu to pad
-			case "6":
-				ChanSpy(event["CallerIDNum"], pacu6, false, true) //connect pacu to pad
-			case "7":
-				ChanSpy(event["CallerIDNum"], pacu7, false, true) //connect pacu to pad
-			case "8":
-				ChanSpy(event["CallerIDNum"], pacu8, false, true) //connect pacu to pad
-			}
 		}
 
 	case "ExtensionStatus":
@@ -87,10 +70,32 @@ func HandleAMI(event map[string]string, conn net.Conn) {
 			}
 		}
 	case "BridgeEnter":
-		lfshook.NewLogger().Infof("=========%s", event["Event"])
+		lfshook.NewLogger().Infof("====BridgeEnter=====%s====%s", event["Event"], event["CallerIDNum"])
 		if utils.IsPAIU(event["CallerIDNum"]) {
+			lfshook.NewLogger().Infof("====BridgeEnter=====%s====%s", event["Event"], event["CallerIDNum"])
 			alstatus.AlarmStatus(event["CallerIDNum"], "connect", conn) // Alarm connected
+
+			switch string(event["CallerIDNum"][2]) { // connect the pacu to pad acording to the pad number
+			case "1":
+				ChanSpy(event["CallerIDNum"], pacu1, false, true) //connect pacu to pad
+			case "2":
+				ChanSpy(event["CallerIDNum"], pacu2, false, true) //connect pacu to pad
+			case "3":
+				ChanSpy(event["CallerIDNum"], pacu3, false, true) //connect pacu to pad
+			case "4":
+				ChanSpy(event["CallerIDNum"], pacu4, false, true) //connect pacu to pad
+			case "5":
+				ChanSpy(event["CallerIDNum"], pacu5, false, true) //connect pacu to pad
+			case "6":
+				ChanSpy(event["CallerIDNum"], pacu6, false, true) //connect pacu to pad
+			case "7":
+				ChanSpy(event["CallerIDNum"], pacu7, false, true) //connect pacu to pad
+			case "8":
+				ChanSpy(event["CallerIDNum"], pacu8, false, true) //connect pacu to pad
+			}
+
 		}
+
 	}
 }
 

+ 14 - 8
internal/app/index.go

@@ -14,13 +14,19 @@ var conn net.Conn
 func StartApp() {
 	mysql.CreateDBInstance()
 
-	conn = stc.CreateConnection("172.16.0.11", 10100)
-
-	go action.StartAMI(func() {
-		lfshook.NewLogger().Info("ami callback")
-		// 首次连接才进行初始化
-	}, []func(event map[string]string){}, conn)
-
-	go stc.StartStcConnection(conn) //connect
+	//conn = stc.CreateConnection("192.168.17.14", 6099)
+	conn = stc.CreateConnection("10.0.11.11", 10100) //172.16.0.11
+	if conn != nil {
+
+		go action.StartAMI(func() {
+			lfshook.NewLogger().Info("ami callback")
+			// 首次连接才进行初始化
+		}, []func(event map[string]string){}, conn)
+
+		go stc.StartStcConnection(conn) //connect
+	} else {
+		conn = stc.CreateConnection("10.0.11.11", 10100)
+		lfshook.NewLogger().Info("reconnect")
+	}
 
 }

+ 59 - 8
internal/app/stc/broadcast/stc-broadcast.go

@@ -8,6 +8,8 @@ import (
 	"pbx-api-gin/internal/app/mysql"
 	msgdata "pbx-api-gin/internal/app/stc/data"
 	"pbx-api-gin/pkg/lfshook"
+	"sync"
+	"time"
 )
 
 var Pacus = []string{"2111", "2121", "2131", "2141", "2151", "2161", "2171", "2181"}
@@ -21,7 +23,9 @@ func HandleStcCmd(conn net.Conn) {
 			fmt.Println("Error reading from server:", err)
 			return
 		}
-		lfshook.NewLogger().Logger.Infof("Get data from STC :%+v", buffer[:n])
+		if buffer[5] != 0x01 {
+			lfshook.NewLogger().Logger.Infof("Get data from STC ===============:%x", buffer[:n])
+		}
 
 		switch buffer[5] {
 		case 0x01: //heartbeat
@@ -48,7 +52,7 @@ func HandleStcCmd(conn net.Conn) {
 			SelfCheck(buffer)
 
 		case 0x0a: //
-			AlarmHandle(buffer[10:])
+			AlarmHandle(buffer)
 
 		case 0x0b: //
 			AlarmResetAll()
@@ -205,25 +209,72 @@ func SelfCheck(data []byte) {
 	}
 }
 
+// 全局变量:记录正在抑制的 exten
+var (
+	suppressedExts = sync.Map{} // map[string]struct{},值存在即表示被抑制
+	suppressionMu  sync.Mutex   // 保护初始化和清理操作(可选)
+)
+
+// suppressKey 生成用于抑制的 key(可以根据需求扩展)
+func suppressKey(exten string, handler byte) string {
+	return fmt.Sprintf("%s_h%x", exten, handler)
+}
+
 // ICP操作乘客报警(根据激活信息判断转到1车还是8车================)
 func AlarmHandle(data []byte) {
 	handler := data[8]
-	extlen := data[9]
-	exten := msgdata.SubstrByRune(string(data[10:]), 0, int(extlen))
+	//extlen := data[9]
+	carr := data[12]
+	pos := data[13]
+	exten := fmt.Sprintf("24%c%c", carr, pos)
+
+	lfshook.NewLogger().Logger.Infof("================ICP Answer PAD================:%s ", exten)
+	key := suppressKey(exten, handler)
+
+	// 只对 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 2 seconds", exten)
+			return // 已存在,说明在2秒窗口期内,直接丢弃
+		}
+
+		// 设置2秒后删除该 key,允许下次通过
+		time.AfterFunc(4*time.Second, func() {
+			suppressedExts.Delete(key)
+			lfshook.NewLogger().Logger.Debugf("Suppression released for key: %s", key)
+		})
+	}
+
+	// 设置2秒后删除该 key,允许下次通过
+	time.AfterFunc(1*time.Second, func() {
+		suppressedExts.Delete(key)
+		lfshook.NewLogger().Logger.Debugf("Suppression released for key: %s", key)
+	})
 
 	switch handler {
-	case 0x01: //answer(ICP+Alarm+PACU==============================)
+	case 0x01: //answer(ICP+Alarm+PACU)
 
-		err := action.Redirect(exten, "2311", "default") // 1车接听
+		err := action.RedirectInQueue(exten, "0402", "ani-rule", "1") // 1车ICP接听
 		if err != nil {
-			lfshook.NewLogger().Info(err)
+			lfshook.NewLogger().Logger.Infof("================ICP Answer PAD====ERR============ : %+v", err.Error())
 		}
 		//invite PACU join in
 		//action.Hangup("PACU")
 		//action.ChanSpy("PACU", exten, false, true)
 
+	case 0x04: //answer(ICP+Alarm+PACU)
+
+		err := action.RedirectInQueue(exten, "0401", "ano-rule", "1") // 1车OCC接听
+		if err != nil {
+			//lfshook.NewLogger().Info(err)
+			lfshook.NewLogger().Logger.Infof("================ICP Answer PAD====ERR============ : %+v", err.Error())
+		}
+		//invite PACU join in
+		//action.Hangup("PACU")
+		//action.ChanSpy("PACU", exten, false, true)
+		lfshook.NewLogger().Logger.Infof("================ICP Answer PAD================:%s ", exten)
 	case 0x02: //hold  重新放回队列里面
-		err := action.Redirect(exten, "0300", "default")
+		err := action.RedirectInQueue(exten, "0300", "default", "1")
 		if err != nil {
 			lfshook.NewLogger().Info(err)
 		}

+ 44 - 0
internal/app/stc/data/msgdata.go

@@ -122,3 +122,47 @@ 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
+
+	// 查找起始标记 0x7f 0x83 0x9d
+	for i := 0; i < len(data)-2; i++ {
+		if data[i] == 0x7f && data[i+1] == 0x83 && data[i+2] == 0x9d {
+			startIdx = i
+			break
+		}
+	}
+
+	if startIdx == -1 {
+		// 没找到起始标记,清掉前面无用数据(防止 OOM)
+		if buf.Len() > 1024 {
+			buf.Next(buf.Len() - 10) // 保留最后10字节继续查找
+		}
+		return nil, fmt.Errorf("no start marker found")
+	}
+
+	// 从起始位置开始查找结束标记 0xFE
+	for i := startIdx; i < len(data); i++ {
+		if data[i] == 0xFE {
+			endIdx = i
+			break
+		}
+	}
+
+	if endIdx == -1 {
+		return nil, fmt.Errorf("no end marker found")
+	}
+
+	// 提取完整包 [startIdx 到 endIdx]
+	packet := make([]byte, endIdx-startIdx+1)
+	copy(packet, data[startIdx:endIdx+1])
+
+	// 从 buffer 中删除已处理的数据
+	buf.Next(endIdx + 1)
+
+	return packet, nil
+}

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

@@ -28,6 +28,7 @@ func StartStcConnection(conn net.Conn) {
 
 func CreateConnection(ServerAddr string, Port int) net.Conn {
 	// connect server
+	fmt.Println("==========conn server============")
 	conn, err := net.DialTCP("tcp", &net.TCPAddr{
 		IP:   net.ParseIP("0.0.0.0"),
 		Port: 10201,
@@ -38,6 +39,7 @@ func CreateConnection(ServerAddr string, Port int) net.Conn {
 	if err != nil {
 		fmt.Println("Error conn server:", err)
 		log.Fatal(err)
+		return nil
 	}
 
 	fileDesc, _ := conn.File()

+ 2 - 1
internal/app/stc/sendstatus/status.go

@@ -14,6 +14,7 @@ func SendToStc(conn net.Conn, data []byte) {
 	if err != nil {
 		fmt.Println("send msg err:", err)
 	}
+	lfshook.NewLogger().Logger.Infof("=data:==%x", data)
 }
 
 // report alarm status to STC
@@ -42,7 +43,7 @@ func AlarmStatus(exten string, status string, conn net.Conn) {
 
 	case "dial": //dial
 		protocol.Data[2] = 0x02
-
+		return
 	case "queue": //PAD alarm
 		protocol.Data[2] = 0x03