|
|
|
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", "0.0.0.0")
|
|
|
|
|
|
|
|
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", nil, 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|