dujunchen 2 viikkoa sitten
vanhempi
commit
20127666af

+ 2 - 0
.gitignore

@@ -1 +1,3 @@
 ./info.log
+./deployments/pbx-panel
+./deployments/pbx-panel-32

BIN
deployments/pbx-panel


+ 5 - 4
internal/app/ami/action/playback.go

@@ -7,15 +7,16 @@ import (
 	"strings"
 )
 
-func Playback(filename string, count int, exten string, timelen int, delay int) (err error) {
+func PlaybackPacu(filename string, count int, delay int, PaType string) (err error) {
 
-	Para := fmt.Sprintf("count=%d,timelen=%d,filename=%s,delay=%d", count, timelen, strings.Replace(filename, ".wav", "", -1), delay)
-	Chan := fmt.Sprintf("Local/%s", exten)
+	Para := fmt.Sprintf("count=%d,filename=%s,delay=%d,priority=%s", count, strings.Replace(filename, ".wav", "", -1), delay, PaType)
+	Chan := "Local/0500" //paging pacu
 
 	action := map[string]string{
 		"Action":   "Originate",
 		"Channel":  Chan,
-		"Exten":    exten,
+		"Exten":    "000",
+		"CallerID": PaType,
 		"Context":  "broadcast-playfile",
 		"Priority": "1",
 		"Variable": Para,

+ 30 - 13
internal/app/ami/index.go

@@ -4,10 +4,10 @@ import (
 	"net"
 	"pbx-api-gin/internal/app/ami/model"
 	"pbx-api-gin/internal/app/mysql"
-	status "pbx-api-gin/internal/app/stc/sendstatus"
-
+	alstatus "pbx-api-gin/internal/app/stc/sendstatus"
 	"pbx-api-gin/internal/pkg/configs"
 	"pbx-api-gin/pkg/lfshook"
+	"pbx-api-gin/pkg/utils"
 
 	"github.com/sirupsen/logrus"
 	"github.com/tqcenglish/amigo-go"
@@ -51,23 +51,40 @@ func StartAMI(connectOKCallBack func(), handleEvents []func(event map[string]str
 func handleAMI(event map[string]string, conn net.Conn) {
 	switch event["Event"] {
 	case "DialBegin":
-		status.AlarmStatus(event["DestCallerIDNum"], event["StatusText"], conn)
+
+		if utils.IsPAIU(event["CallerIDNum"]) && utils.IsICP(event["DestCallerIDNum"]) {
+
+			alstatus.AlarmStatus(event["CallerIDNum"], "dial", conn) // Alarm dial ICP start
+		}
+
 	case "DialEnd":
-		//stc.AlarmStatus(event["DestCallerIDNum"], event["StatusText"], conn) //stop dial ICP(Reset dial status)
-	case "ExtensionStatus":
 
-		status := &model.Extension{
-			Extension: event["Exten"],
-			Status:    event["StatusText"],
+		if utils.IsPAIU(event["CallerIDNum"]) {
+			alstatus.AlarmStatus(event["CallerIDNum"], "Idle", conn) // Alarm dial end
 		}
-		_, err := mysql.DBOrmInstance.Where("exten = ?", status.Extension).Cols("status").Update(status)
-		if err != nil {
-			lfshook.NewLogger().Infof("update extension status err : %+v", err.Error())
+	case "QueueCallerJoin":
+
+		if utils.IsPAIU(event["CallerIDNum"]) {
+			alstatus.AlarmStatus(event["CallerIDNum"], "queue", conn) // Alarm join the queue
+		}
+	case "ExtensionStatus":
+
+		if event["StatusText"] == "Idle" || event["StatusText"] == "Unavailable" {
+			status := &model.Extension{
+				Extension: event["Exten"],
+				Status:    event["StatusText"],
+			}
+			_, err := mysql.DBOrmInstance.Where("exten = ?", status.Extension).Cols("status").Update(status)
+			if err != nil {
+				lfshook.NewLogger().Infof("update extension status err : %+v", err.Error())
+			}
+			alstatus.AlarmStatus(event["Exten"], event["StatusText"], conn) // Alarm idle + unavailable
 		}
-		//stc.AlarmStatus(event["Exten"], event["StatusText"], conn) // idle + unavailable
 	case "BridgeEnter":
 
-		//stc.AlarmStatus(event["CallerIDNum"], event["StatusText"], conn) // alarm connected
+		if utils.IsPAIU(event["CallerIDNum"]) {
+			alstatus.AlarmStatus(event["CallerIDNum"], "connect", conn) // Alarm connected
+		}
 	}
 }
 

+ 2 - 1
internal/app/ami/model/sip.go

@@ -25,7 +25,8 @@ import "encoding/json"
 // ActiveChannels:
 type Extension struct {
 	Extension string `xorm:"exten" json:"exten"`
-	Type      string `xorm:"type" json:"type"`
+	ExtType   string `xorm:"exttype" json:"extType"`
+	PaType    string `xorm:"patype" json:"paType"`
 	Priority  string `xorm:"priority" json:"priority"`
 	Special   int    `xorm:"special" json:"special"`
 	Status    string `xorm:"status" json:"status"`

+ 157 - 87
internal/app/stc/broadcast/stc-broadcast.go

@@ -1,15 +1,18 @@
 package broadcast
 
 import (
-	"bytes"
 	"fmt"
 	"net"
 	"pbx-api-gin/internal/app/ami/action"
 	"pbx-api-gin/internal/app/ami/model"
 	"pbx-api-gin/internal/app/mysql"
+	msgdata "pbx-api-gin/internal/app/stc/data"
 	"pbx-api-gin/pkg/lfshook"
 )
 
+var BroadcastExtens = []string{"2111", "2121", "2131", "2141", "2151", "2161", "2171", "2181"}
+var AlarmExtens = []string{"2111", "2121", "2131", "2141", "2151", "2161", "2171", "2181"}
+
 func HandleStcCmd(conn net.Conn) {
 
 	for {
@@ -25,61 +28,60 @@ func HandleStcCmd(conn net.Conn) {
 		case 0x01: //heartbeat
 
 		case 0x02: //STN
-			StationAnn([4]byte{buffer[8], buffer[9], buffer[10], buffer[11]})
+			StationAnn(buffer)
 
 		case 0x03: //ACTIVE
 			Active([1]byte{buffer[8]})
 
 		case 0x05: //SPC
-			SpecialAnn([4]byte{buffer[8], buffer[9], buffer[10], buffer[11]})
+			SpecialAnn(buffer)
 
 		case 0x06: //EMG
-			EmgMsg([4]byte{buffer[8], buffer[9], buffer[10], buffer[11]})
+			EmgMsg(buffer)
+
 		case 0x07: //STOP
 			AnnStop([4]byte{buffer[8], buffer[9], buffer[10], buffer[11]})
+
 		case 0x08: //DCS
-			DcsAnn([4]byte{buffer[8], buffer[9], buffer[10], buffer[11]})
+			DcsAnn(buffer)
+
 		case 0x09: //SELF CHECK
-			SelfCheck([4]byte{buffer[8], buffer[9], buffer[10], buffer[11]})
+			SelfCheck(buffer)
+
 		case 0x0a: //
-			AlarmHandle([4]byte{buffer[8], buffer[9], buffer[10], buffer[11]})
+			AlarmHandle(buffer[10:])
+
 		case 0x0b: //
-			AlarmResetAll([4]byte{buffer[8], buffer[9], buffer[10], buffer[11]})
+			AlarmResetAll()
+
 		case 0x0c: //
-			RecordStorageConf([4]byte{buffer[8], buffer[9], buffer[10], buffer[11]})
+			RecordStorageConf(buffer[8:])
 		}
 	}
 }
 
-/*
-	func SendToStc(conn net.Conn, buff []byte) {
-		_, err := conn.Write(buff)
-		if err != nil {
-			fmt.Println("send msg err:", err)
-		}
-	}
-*/
+// STN , 自动报站广播
+func StationAnn(data []byte) (err error) {
 
-func StationAnn(data [4]byte) (err error) {
-	specialVoice := int(data[0])
-	Exten := "2411"
-	delay := data[1]
-	cycleCount := data[2]
-	timeLen := data[3]
+	specialVoice := int(data[8])
+	delay := data[9]
+	cycleCount := data[10]
+	datalen := int(data[11])
 
-	filename := "welcome"
+	filename := msgdata.SubstrByRune(string(data[12:]), 0, datalen-4)
 
 	//update special voice
-	_, er := mysql.DBOrmInstance.Where("exten = ?", Exten).Update(&model.Extension{Special: specialVoice})
+	_, er := mysql.DBOrmInstance.In("exten", BroadcastExtens).Update(&model.Extension{Special: specialVoice, PaType: "STN"})
 	if er != nil {
 		lfshook.NewLogger().Logger.Infof("update special voice to exten err : %+v", er.Error())
 		return er
 	}
 
-	action.Playback(filename, int(cycleCount), Exten, int(timeLen), int(delay))
+	action.PlaybackPacu(filename, int(cycleCount), int(delay), "STN")
 	return nil
 }
 
+// 激活信号
 func Active(data [1]byte) {
 
 	Num := int(data[0])
@@ -93,105 +95,173 @@ func Active(data [1]byte) {
 
 }
 
-func SpecialAnn(data [4]byte) {
-	//specialVoice := int(data[0])
-	Exten := "2411"
-	delay := data[0]
-	cycleCount := data[1]
-	timeLen := data[2]
+// SPC ,特殊服务消息广播
+func SpecialAnn(data []byte) {
+
+	delay := data[8]
+	cycleCount := data[9]
+	datalen := int(data[10])
 
-	filename := "welcome"
+	filename := msgdata.SubstrByRune(string(data[11:]), 0, datalen-3)
+
+	//update pa type
+	_, er := mysql.DBOrmInstance.In("exten", BroadcastExtens).Update(&model.Extension{PaType: "SPC"})
+	if er != nil {
+		lfshook.NewLogger().Logger.Infof("update special voice to exten err : %+v", er.Error())
+	}
 
 	if int(cycleCount) == 255 {
-		action.Playback(filename, 9999999, Exten, int(timeLen), int(delay))
+		action.PlaybackPacu(filename, 9999999, int(delay), "SPC")
 	} else {
-		action.Playback(filename, int(cycleCount), Exten, int(timeLen), int(delay))
+		action.PlaybackPacu(filename, int(cycleCount), int(delay), "SPC")
 	}
-
 }
 
-func EmgMsg(data [4]byte) {
-	//specialVoice := int(data[0])
-	Exten := "2411"
-	delay := data[0]
-	cycleCount := data[1]
-	timeLen := data[2]
+// EMG ,紧急服务消息广播
+func EmgMsg(data []byte) {
+	delay := data[8]
+	cycleCount := data[9]
+	datalen := int(data[10])
 
-	filename := "welcome"
+	filename := msgdata.SubstrByRune(string(data[11:]), 0, datalen-3)
+
+	//update pa type
+	_, er := mysql.DBOrmInstance.In("exten", BroadcastExtens).Update(&model.Extension{PaType: "EMG"})
+	if er != nil {
+		lfshook.NewLogger().Logger.Infof("update special voice to exten err : %+v", er.Error())
+	}
 
 	if int(cycleCount) == 255 {
-		action.Playback(filename, 9999999, Exten, int(timeLen), int(delay))
+		action.PlaybackPacu(filename, 9999999, int(delay), "EMG")
 	} else {
-		action.Playback(filename, int(cycleCount), Exten, int(timeLen), int(delay))
+		action.PlaybackPacu(filename, int(cycleCount), int(delay), "EMG")
 	}
 }
 
+// 停止指定类型广播
 func AnnStop(data [4]byte) {
-	if data[0] == 0x03 {
-		//asterisk.Hangup("2412")
-	} else if data[0] == 0x04 {
-		//asterisk.Hangup("2412")
-	} else if data[0] == 0x07 {
-		//asterisk.Hangup("2412")
-	} else if data[0] == 0x08 {
-		//asterisk.Hangup("2412")
-	} else if data[0] == 0x09 {
-		//asterisk.Hangup("2412")
+	switch data[0] {
+	case 0x03:
+
+	case 0x04:
+
+	case 0x07:
+
+	case 0x08:
+
+	case 0x09:
+
+	}
+	for _, ext := range BroadcastExtens {
+		action.Hangup(ext)
+	}
+	//update pa type
+	_, er := mysql.DBOrmInstance.Cols("patype").In("exten", BroadcastExtens).Update(&model.Extension{PaType: ""})
+	if er != nil {
+		lfshook.NewLogger().Logger.Infof("update special voice to exten err : %+v", er.Error())
 	}
 }
 
-func DcsAnn(data [4]byte) {
-	Exten := "2411"
-	delay := data[0]
-	cycleCount := data[1]
-	timeLen := data[2]
+// DCS 语音
+func DcsAnn(data []byte) {
+	delay := data[8]
+	cycleCount := data[9]
+	datalen := int(data[10])
 
-	filename := "welcome"
+	filename := msgdata.SubstrByRune(string(data[11:]), 0, datalen-3)
 
-	if cycleCount == 255 {
-		action.Playback(filename, 9999999, Exten, int(timeLen), int(delay))
+	//update pa type
+	_, er := mysql.DBOrmInstance.In("exten", BroadcastExtens).Update(&model.Extension{PaType: "DCS"})
+	if er != nil {
+		lfshook.NewLogger().Logger.Infof("update special voice to exten err : %+v", er.Error())
+	}
+
+	if int(cycleCount) == 255 {
+		action.PlaybackPacu(filename, 9999999, int(delay), "DCS")
 	} else {
-		action.Playback(filename, int(cycleCount), Exten, int(timeLen), int(delay))
+		action.PlaybackPacu(filename, int(cycleCount), int(delay), "DCS")
 	}
 }
 
-func SelfCheck(data [4]byte) {
-	check := data[0]
-	delay := data[1]
-	cycleCount := data[2]
-	timeLen := data[3]
-	filename := "welcome"
-	Exten := ""
-
-	if check == 0x01 {
-		action.Playback(filename, int(cycleCount), Exten, int(timeLen), int(delay))
-	} else if check == 0x02 {
+// 自检广播
+func SelfCheck(data []byte) {
+	check := data[8]
+	delay := data[9]
+	cycleCount := data[10]
+	datalen := int(data[11])
+
+	filename := msgdata.SubstrByRune(string(data[12:]), 0, datalen-4)
+
+	//update pa type
+	_, er := mysql.DBOrmInstance.In("exten", BroadcastExtens).Update(&model.Extension{PaType: "CHK"})
+	if er != nil {
+		lfshook.NewLogger().Logger.Infof("update special voice to exten err : %+v", er.Error())
+	}
+
+	switch check {
+	case 0x01:
+		action.PlaybackPacu(filename, int(cycleCount), int(delay), "CHK")
+	case 0x02:
 		//asterisk.Hangup(Exten)
 	}
 }
 
-func AlarmHandle(data [4]byte) {
-	handler := data[0]
-	//len := data[1]
-	//src := data[2]
+// ICP操作乘客报警(根据激活信息判断转到1车还是8车================)
+func AlarmHandle(data []byte) {
+	handler := data[8]
+	extlen := data[9]
+	exten := msgdata.SubstrByRune(string(data[10:]), 0, int(extlen))
 
 	switch handler {
-	case 0x01: //answer
+	case 0x01: //answer(ICP+Alarm+PACU==============================)
 
-	case 0x02: //hold
+		err := action.Redirect(exten, "2311", "default") // 1车接听
+		if err != nil {
+			lfshook.NewLogger().Info(err)
+		}
+		//invite PACU join in
+		//action.Hangup("PACU")
+		//action.ChanSpy("PACU", exten, false, true)
 
-	case 0x03: //hangup
+	case 0x02: //hold  重新放回队列里面
+		err := action.Redirect(exten, "0300", "default")
+		if err != nil {
+			lfshook.NewLogger().Info(err)
+		}
 
+	case 0x03: //hangup
+		action.Hangup(exten)
 	}
 }
 
-func AlarmResetAll(data [4]byte) {
+// 挂断所有报警器
+func AlarmResetAll() {
 
-	if bytes.Equal(data[:], make([]byte, len(data))) {
-		//reset all the alarm
+	var AlarmExts []model.Extension
+
+	er := mysql.DBOrmInstance.Where("exttype = ?", "PAIU").Find(&AlarmExts)
+	if er != nil {
+		lfshook.NewLogger().Logger.Infof("update special voice to exten err : %+v", er.Error())
 	}
-}
 
-func RecordStorageConf(data [4]byte) {
+	for _, ext := range AlarmExts {
+		action.Hangup(ext.Extension)
+	}
+}
 
+func RecordStorageConf(data []byte) {
+	/*padRcd := data[0]
+	padRcdStorage := data[1]
+	paRcdStorage := data[2]
+	cpaRcdStorage := data[3]
+	padRcdDel := data[4]
+	PaRcdDel := data[5]
+	cpaRcdDel := data[6]
+
+	//update pa type
+	_, er := mysql.DBOrmInstance.In("exten", BroadcastExtens).Update(&model.Extension{PaType: "CHK"})
+	if er != nil {
+		lfshook.NewLogger().Logger.Infof("update special voice to exten err : %+v", er.Error())
+	}
+	*/
 }

+ 12 - 1
internal/app/stc/data/msgdata.go

@@ -1,4 +1,4 @@
-package data
+package msgdata
 
 import (
 	"bytes"
@@ -111,3 +111,14 @@ func Decode(data []byte) (*Protocol, error) {
 
 	return p, nil
 }
+func SubstrByRune(s string, start, length int) string {
+	runes := []rune(s)
+	if start >= len(runes) {
+		return ""
+	}
+	end := start + length
+	if end > len(runes) {
+		end = len(runes)
+	}
+	return string(runes[start:end])
+}

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

@@ -5,7 +5,7 @@ import (
 	"log"
 	"net"
 	"pbx-api-gin/internal/app/stc/broadcast"
-	"pbx-api-gin/internal/app/stc/data"
+	msgdata "pbx-api-gin/internal/app/stc/data"
 
 	"syscall"
 	"time"
@@ -50,7 +50,7 @@ func CreateConnection(ServerAddr string, Port int) net.Conn {
 func Sendheartbeat(conn net.Conn) {
 	var count uint8
 	//init heartbeat data
-	protocol := data.NewProtocol()
+	protocol := msgdata.NewProtocol()
 	protocol.SourceID = 0x02
 	protocol.DestinationID = 0x01
 	protocol.MessageID = 0x21
@@ -75,3 +75,15 @@ func Sendheartbeat(conn net.Conn) {
 		}
 	}
 }
+
+// 检查PA server是主状态还是从状态
+func CheckMaster(conn net.Conn) bool {
+	//	var count uint8
+	//init heartbeat data
+	protocol := msgdata.NewProtocol()
+	protocol.SourceID = 0x02
+	protocol.DestinationID = 0x01
+	protocol.MessageID = 0x21
+	protocol.DataLength = 0x04
+	return false
+}

+ 108 - 16
internal/app/stc/sendstatus/status.go

@@ -1,34 +1,126 @@
-package status
+package alstatus
 
 import (
+	"fmt"
 	"net"
-	"pbx-api-gin/internal/app/stc/data"
+	"pbx-api-gin/api/model"
+	"pbx-api-gin/internal/app/mysql"
+	msgdata "pbx-api-gin/internal/app/stc/data"
+	"pbx-api-gin/pkg/lfshook"
 )
 
+func SendToStc(conn net.Conn, data []byte) {
+
+	_, err := conn.Write(data)
+	if err != nil {
+		fmt.Println("send msg err:", err)
+	}
+}
+
+// report alarm status to STC
 func AlarmStatus(exten string, status string, conn net.Conn) {
 
-	//ext, _ := strconv.Atoi(exten)
-	//if ext
-	//len := data[1]
-	//src := data[2]
-	protocol := data.NewProtocol()
+	//check exten if it is a alarm exten
+	var data model.Extension
+	_, er := mysql.DBOrmInstance.Where("exttype = ? and exten = ?", "PAIU", exten).Get(&data)
+	if er != nil {
+		lfshook.NewLogger().Logger.Infof("get alarm exten err : %+v", er.Error())
+	}
+
+	if len(data.Extension) == 0 {
+		return
+	}
+
+	protocol := msgdata.NewProtocol()
 	protocol.MessageID = 0x26
 	protocol.DataLength = 0x04
 
-	protocol.Data[0] = byte(int(exten[3] - 0))
-	protocol.Data[1] = byte(int(exten[2] - 0))
+	protocol.Data[0] = byte(int(exten[3] - 0)) //车厢号
+	protocol.Data[1] = byte(int(exten[2] - 0)) //位置号
 
+	//报警器工作状态
 	switch status {
-	case "Unavailable": //offline
-		protocol.Data[2] = 0x01
-	case "idle": //idle
+	case "unavailable", "Unavailable": //offline
 		protocol.Data[2] = 0x00
-	case "dial": //dial
-		protocol.Data[2] = 0x01
-	case "Hold": //hold
+	case "idle", "Idle": //idle
 		protocol.Data[2] = 0x01
-	case "InUse", "Busy": //connect
+	case "dial": //dial
+		protocol.Data[2] = 0x02
+		//invite the PACU to queue
+		//action.Dial("PACU", "0300", "default", "0300", "0300", "PJSIP")
+	case "queue": //hold
+		protocol.Data[2] = 0x03
+	case "connect": //connect
+		protocol.Data[2] = 0x04
+	}
+
+	encoded, errEn := protocol.Encode()
+	if errEn != nil {
+		fmt.Println("Encode error:", errEn)
+		return
+	}
+
+	SendToStc(conn, encoded)
+}
+
+// report broadcast status to STC
+func PaStatus(src string, patype string, operation string, conn net.Conn) {
+
+	protocol := msgdata.NewProtocol()
+	protocol.MessageID = 0x22
+	protocol.DataLength = 0x04
+
+	//广播发起方
+	switch src {
+	case "2311": //offline
+		protocol.Data[0] = 0x01
+	case "2381": //idle
+		protocol.Data[0] = 0x08
+	case "": //idle
+		protocol.Data[0] = 0x00
+	}
+
+	//广播类型
+	switch patype {
+	case "C2C": //司机对讲
+		protocol.Data[1] = 0x01
+	case "C2P": //人工广播
+		protocol.Data[1] = 0x02
+	case "DOR": //开关门提示音
+		protocol.Data[1] = 0x03
+	case "EMG": //紧急广播
+		protocol.Data[1] = 0x04
+	case "AlARM": //报警
+		protocol.Data[1] = 0x05
+	case "CPA": //地面广播
+		protocol.Data[1] = 0x06
+	case "SPE": //特殊
+		protocol.Data[1] = 0x07
+	case "STN": //报站
+		protocol.Data[1] = 0x08
+	case "CH": //自检
+		protocol.Data[1] = 0x09
+	}
+
+	//操作类型
+	switch operation {
+	case "start": //
 		protocol.Data[2] = 0x01
+	case "end": //
+		protocol.Data[2] = 0x02
+	case "refuse": //
+		protocol.Data[2] = 0x03
+	case "fail": //
+		protocol.Data[2] = 0x04
+	case "continue": //
+		protocol.Data[2] = 0x05
+	}
+
+	encoded, errEn := protocol.Encode()
+	if errEn != nil {
+		fmt.Println("Encode error:", errEn)
+		return
 	}
 
+	SendToStc(conn, encoded)
 }

+ 18 - 0
pkg/utils/utils.go

@@ -108,6 +108,24 @@ func IsChannel(input string) bool {
 	return false
 }
 
+// 判断是否是广播分机
+func IsPAIU(input string) bool {
+
+	return input[:2] == "24"
+}
+
+// 判断是否是广播分机
+func IsPACU(input string) bool {
+
+	return input[:2] == "21"
+}
+
+// 判断是否是司机控制盒
+func IsICP(input string) bool {
+
+	return input[:2] == "23"
+}
+
 // 判断文件是否存在
 func PathExists(path string) bool {
 	_, err := os.Stat(path)