package common import ( "bufio" "crypto/rand" "encoding/binary" "encoding/hex" "fmt" "io" "net" "strings" "git.cheetah.cat/cheetah/opentetraflex-go/common/tmkind" ) type TerminalClient struct { address string conn net.Conn netWriter *bufio.Writer netReader *bufio.Reader HandlerMap map[uint32]chan HandlerResponse RX chan *TerminalMessageResponse GroupConfig []GroupConfig RegistrationInfo RegistrationInfo udpRTP_TX *net.UDPConn } func NewTerminalClient(address string) (tc *TerminalClient) { return &TerminalClient{ address: address, RX: make(chan *TerminalMessageResponse), HandlerMap: make(map[uint32]chan HandlerResponse), } } func (tc *TerminalClient) Connect() (err error) { tc.conn, err = net.Dial("tcp", tc.address) if err != nil { return err } tc.netWriter = bufio.NewWriter(tc.conn) tc.netReader = bufio.NewReader(tc.conn) go tc.rxLoop() return nil } func (tc *TerminalClient) Send(message *TerminalMessage) (err error) { _, err = tc.netWriter.Write(message.Encode()) if err != nil { return err } err = tc.netWriter.Flush() if err != nil { return err } return nil } func (tc *TerminalClient) Authenticate(addr TetraFlexAddress) (err error) { randomBytes := make([]byte, 10) _, err = rand.Read(randomBytes) if err != nil { return err } // 20160111 tfAuthReq, err := NewTMAuthReq(addr, 0, randomBytes, 1, "OpenTetraFlex-Go", 20230831, "1.0.0") if err != nil { return err } tc.HandlerMap[tfAuthReq.handler] = make(chan HandlerResponse) //fmt.Println(hex.EncodeToString(tfAuthReq.Encode())) err = tc.Send(tfAuthReq) if err != nil { return err } response := <-tc.HandlerMap[tfAuthReq.handler] if !response.Success { return response.Error } tc.RegistrationInfo, err = response.TMR.AsRegistrationInfo() if err != nil { return err } return nil } func (tc *TerminalClient) SubscribeEvents(messageTypes []tmkind.TetraFlexTerminalMessageKinds) (err error) { tfNotReq, err := NewTMNotificationRequest(messageTypes) if err != nil { return err } tc.HandlerMap[tfNotReq.handler] = make(chan HandlerResponse) err = tc.Send(tfNotReq) if err != nil { return err } response := <-tc.HandlerMap[tfNotReq.handler] if !response.Success { return response.Error } return nil } func (tc *TerminalClient) AttachGroups(groups []GroupConfig) (err error) { tfGroupAttachReq, err := NewTMIpApiGroupAttachRequest(groups) if err != nil { return nil } fmt.Println(hex.EncodeToString(tfGroupAttachReq.Encode())) tc.HandlerMap[tfGroupAttachReq.handler] = make(chan HandlerResponse) //fmt.Println(hex.EncodeToString(tfAuthReq.Encode())) err = tc.Send(tfGroupAttachReq) if err != nil { return err } response := <-tc.HandlerMap[tfGroupAttachReq.handler] if !response.Success { return response.Error } return nil } func (tc *TerminalClient) CallSetup(address TetraFlexAddress, callType TetraFlexCallTypes, callPriority TetraFlexCallPriorities, hookCall bool, identityInfo TetraFlexIdentityInfo) (callRef byte, err error) { tfCallSetupReq, err := NewTMIpApiCallSetupRequest(address, callType, callPriority, hookCall, identityInfo) if err != nil { return callRef, err } fmt.Println(hex.EncodeToString(tfCallSetupReq.Encode())) tc.HandlerMap[tfCallSetupReq.handler] = make(chan HandlerResponse) //fmt.Println(hex.EncodeToString(tfAuthReq.Encode())) err = tc.Send(tfCallSetupReq) if err != nil { return callRef, err } response := <-tc.HandlerMap[tfCallSetupReq.handler] if !response.Success { return callRef, response.Error } //fmt.Println(hex.EncodeToString(response.TMR.payload)) callRef, err = response.TMR.readByte() if err != nil { return callRef, err } return callRef, nil } func (tc *TerminalClient) CallDisconnect(callRef byte, disconnectCause TetraFlexCallDisconnectCauses) (err error) { tfCallDisconnectReq, err := NewTMIpApiCallDisconnectRequest(callRef, disconnectCause) if err != nil { return nil } fmt.Println(hex.EncodeToString(tfCallDisconnectReq.Encode())) tc.HandlerMap[tfCallDisconnectReq.handler] = make(chan HandlerResponse) //fmt.Println(hex.EncodeToString(tfAuthReq.Encode())) err = tc.Send(tfCallDisconnectReq) if err != nil { return err } response := <-tc.HandlerMap[tfCallDisconnectReq.handler] if !response.Success { return response.Error } return nil } func (tc *TerminalClient) CallConnect(callRef byte) (err error) { tfCallConnectReq, err := NewTMIpApiCallConnectRequest(callRef) if err != nil { return nil } fmt.Println(hex.EncodeToString(tfCallConnectReq.Encode())) tc.HandlerMap[tfCallConnectReq.handler] = make(chan HandlerResponse) //fmt.Println(hex.EncodeToString(tfAuthReq.Encode())) err = tc.Send(tfCallConnectReq) if err != nil { return err } response := <-tc.HandlerMap[tfCallConnectReq.handler] if !response.Success { return response.Error } return nil } func (tc *TerminalClient) CallStreamRequest(uint1 uint, callRef byte, streamRef byte, audioChannelType TetraFlexAudioChannelTypes) (err error) { tfStreamReq, err := NewTMIpApiCallStreamRequest(uint1, callRef, streamRef, audioChannelType) if err != nil { return nil } fmt.Println(hex.EncodeToString(tfStreamReq.Encode())) tc.HandlerMap[tfStreamReq.handler] = make(chan HandlerResponse) //fmt.Println(hex.EncodeToString(tfAuthReq.Encode())) err = tc.Send(tfStreamReq) if err != nil { return err } response := <-tc.HandlerMap[tfStreamReq.handler] if !response.Success { return response.Error } return nil } func (tc *TerminalClient) PTTRequest(callRef byte, identityInfo TetraFlexIdentityInfo, octaStateByte byte) (err error) { // IpApiCallPttRequest tfPTTRequest, err := NewTMIpApiCallPTTRequest(callRef, identityInfo, octaStateByte) if err != nil { return err } fmt.Println(hex.EncodeToString(tfPTTRequest.Encode())) tc.HandlerMap[tfPTTRequest.handler] = make(chan HandlerResponse) //fmt.Println(hex.EncodeToString(tfAuthReq.Encode())) err = tc.Send(tfPTTRequest) if err != nil { return err } response := <-tc.HandlerMap[tfPTTRequest.handler] if !response.Success { return response.Error } return nil } func (tc *TerminalClient) PTTRelease(callRef byte) (err error) { // IpApiCallPttRequest tfPTTRelease, err := NewTMIpApiCallPTTRelease(callRef) if err != nil { return err } fmt.Println(hex.EncodeToString(tfPTTRelease.Encode())) tc.HandlerMap[tfPTTRelease.handler] = make(chan HandlerResponse) //fmt.Println(hex.EncodeToString(tfAuthReq.Encode())) err = tc.Send(tfPTTRelease) if err != nil { return err } response := <-tc.HandlerMap[tfPTTRelease.handler] if !response.Success { return response.Error } return nil } func (tc *TerminalClient) RTPConnect() (err error) { portNum := tc.RegistrationInfo.RTPPorts[0] localAddress, _ := net.ResolveUDPAddr("udp", fmt.Sprintf("0.0.0.0:%d", portNum)) parts := strings.Split(tc.address, ":") remoteTXAddr := fmt.Sprintf("%s:%d", parts[0], portNum) remoteAddress, _ := net.ResolveUDPAddr("udp", remoteTXAddr) tc.udpRTP_TX, err = net.DialUDP("udp", localAddress, remoteAddress) if err != nil { return err } return nil } func (tc *TerminalClient) RTPSend(data []byte) (err error) { //parts := strings.Split(tc.address, ":") //remoteTXAddr := fmt.Sprintf("%s:%d", parts[0], tc.RegistrationInfo.RTPPorts[0]) //remoteAddress, _ := net.ResolveUDPAddr("udp", remoteTXAddr) _, err = tc.udpRTP_TX.Write(data) //WriteToUDP(data, remoteAddress) if err != nil { return err } return nil } func (tc *TerminalClient) RTPConnected() bool { if tc.udpRTP_TX != nil { return true } return false } /* groupAddr := common.NewAddressFromSSINumber(103) identityNum := common.NewAddressFromSSINumber(1007) response, err = common.NewTMIpApiCallSetupRequest(*groupAddr, common.CallTypeGroup, common.CallPriorityDefault, false, common.TetraFlexIdentityInfo{ Description: "blurb", Kind: common.IdentityKindTerminal, Address: *identityNum, UnifiedSSIKinds: common.UnifiedKindPersonalNumber, }) if err != nil { panic(err) } fmt.Println(hex.EncodeToString(response.Encode())) terminalClient.Send(response) */ func (tc *TerminalClient) rxLoop() { var err error for { dataHeader := make([]byte, 2) //2 bytes for uint16 _, err = io.ReadFull(tc.netReader, dataHeader) if err != nil { panic(err) } payloadLength := binary.LittleEndian.Uint16(dataHeader) fmt.Printf("length: %d bytes\n", payloadLength) kindData := make([]byte, 2) //2 bytes for uint16 _, err = io.ReadFull(tc.netReader, kindData) if err != nil { panic(err) } messageKind := binary.LittleEndian.Uint16(kindData) fmt.Printf("kind: %d\n", messageKind) handlerData := make([]byte, 4) //4 bytes for uint32 _, err = io.ReadFull(tc.netReader, handlerData) if err != nil { panic(err) } messageHandler := binary.LittleEndian.Uint32(handlerData) fmt.Printf("handler: %d\n", messageHandler) dataPayload := make([]byte, payloadLength-(2+2+4)) //8 bytes for header _, err = io.ReadFull(tc.netReader, dataPayload) if err != nil { panic(err) } parsedResp, err := NewTerminalMessageResponse(payloadLength, tmkind.TetraFlexTerminalMessageKinds(messageKind), messageHandler, dataPayload) if err != nil { panic(err) } if _, ok := tc.HandlerMap[messageHandler]; ok { tc.HandlerMap[messageHandler] <- HandlerResponse{ Success: true, TMR: parsedResp, } delete(tc.HandlerMap, messageHandler) } else { // Lets Handle KeepAlive for ourself if parsedResp.Kind == tmkind.IpApiKeepAliveChallenge { challenge, err := parsedResp.AsKeepAliveChallenge() if err != nil { panic(err) } response, err := NewTMKeepAliveResponse(challenge) if err != nil { panic(err) } err = tc.Send(response) if err != nil { panic(err) } continue } tc.RX <- parsedResp } } }