func (uplink *SenseUplink) AddNewObject(handle int, t ObjectType, id string, enigmaobject interface{}) (*Object, error) {
	obj := NewObject(handle, t, id, enigmaobject)
	if err := uplink.Objects.AddObject(obj); err != nil {
		return nil, errors.WithStack(err)
	}
	return obj, nil
}
AddNewObject to object list
package enigmahandlers
import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net/http"
	neturl "net/url"
	"strconv"
	"strings"
	"time"
	jsoniter "github.com/json-iterator/go"
	"github.com/pkg/errors"
	"github.com/qlik-oss/enigma-go/v2"
	"github.com/qlik-oss/gopherciser/enigmainterceptors"
	"github.com/qlik-oss/gopherciser/globals"
	"github.com/qlik-oss/gopherciser/globals/constant"
	"github.com/qlik-oss/gopherciser/helpers"
	"github.com/qlik-oss/gopherciser/logger"
	"github.com/qlik-oss/gopherciser/requestmetrics"
	"github.com/qlik-oss/gopherciser/senseobjects"
)
type (
	SenseUplink struct {
		Global     *enigma.Global
		CurrentApp *senseobjects.App
		Objects    ObjectsMap
		FieldCache FieldCache
		VarCache   VarCache
		Traffic    ITrafficLogger
		ctx                context.Context
		cancel             context.CancelFunc
		logEntry           *logger.LogEntry
		trafficMetrics     *requestmetrics.RequestMetrics
		failedConnectFuncs []func()
		MockMode bool
	}
	Cache struct {
		Field FieldCache
		Var   VarCache
	}
	SenseConnection struct {
		*SenseUplink
	}
	OnAuthenticationInformation struct {
		MustAuthenticate bool `json:"mustAuthenticate"`
	}
	OnConnected struct {
		SessionState string `json:"qSessionState"`
	}
)
const (
	MaxRetries = 3
)
var jsonit = jsoniter.ConfigCompatibleWithStandardLibrary
func NewSenseUplink(ctx context.Context, logentry *logger.LogEntry, metrics *requestmetrics.RequestMetrics, trafficLogger ITrafficLogger) *SenseUplink {
	cCtx, cancel := context.WithCancel(ctx)
	return &SenseUplink{
		ctx:            cCtx,
		cancel:         cancel,
		trafficMetrics: metrics,
		logEntry:       logentry,
		Traffic:        trafficLogger,
		FieldCache:     NewFieldCache(),
		VarCache:       NewVarCache(),
	}
}
func (connection *SenseConnection) Disconnect() error {
	if connection == nil || connection.SenseUplink == nil {
		return nil
	}
	connection.SenseUplink.Disconnect()
	return nil
}
func (connection *SenseConnection) SetSense(uplink *SenseUplink) {
	if connection.SenseUplink != nil {
		connection.SenseUplink.Disconnect()
	}
	connection.SenseUplink = uplink
}
func (connection *SenseConnection) Sense() *SenseUplink {
	return connection.SenseUplink
}
func (uplink *SenseUplink) Connect(ctx context.Context, url string, headers http.Header, cookieJar http.CookieJar, allowUntrusted bool, timeout time.Duration) error {
	if uplink.Global != nil {
		uplink.Global.DisconnectFromServer()
		uplink.Global = nil
	}
	dialer := enigma.Dialer{
		MockMode: uplink.MockMode,
		Interceptors: []enigma.Interceptor{
			(&enigmainterceptors.MetricsHandler{
				Log: uplink.LogMetric,
			}).MetricsInterceptor,
			uplink.retryInterceptor,
		},
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: allowUntrusted,
		},
	}
	if cookieJar != nil {
		dialer.Jar = cookieJar
	}
	dialer.TrafficLogger = uplink.Traffic
	onUnexpectedDisconnect := func() {
		if helpers.IsContextTriggered(uplink.ctx) {
			return
		}
		uplink.executeFailedConnectFuncs()
	}
	setupDialer(&dialer, timeout, uplink.logEntry, onUnexpectedDisconnect)
	startTimestamp := time.Now()
	global, err := dialer.Dial(ctx, url, headers)
	if err != nil {
		return errors.Wrap(err, "Error connecting to Sense")
	}
	u, err := neturl.Parse(url)
	if err != nil {
		uplink.logEntry.Logf(logger.WarningLevel, "error<%v> resolving url<%s>", err, url)
	} else if dialer.Jar != nil {
		switch u.Scheme {
		case "wss":
			u.Scheme = "https"
		case "ws":
			u.Scheme = "http"
		}
	}
	if err := uplink.trafficMetrics.Update(startTimestamp, time.Now(), 0, 0); err != nil {
		return errors.WithStack(err)
	}
	connectMsgChan := global.SessionMessageChannel(globals.EventTopics...)
	defer global.CloseSessionMessageChannel(connectMsgChan)
	go func() {
		sessionChan := global.SessionMessageChannel()
		for {
			select {
			case event, ok := <-sessionChan:
				if !ok {
					return
				}
				if err := uplink.trafficMetrics.UpdateReceived(time.Now(), int64(len(event.Content))); err != nil {
					uplink.logEntry.LogError(err)
				}
			case <-uplink.ctx.Done():
				return
			}
		}
	}()
	uplink.Global = global
	if !uplink.MockMode {
		_, connectErr := global.EngineVersion(uplink.ctx)
		mustAuthenticate, onConnectedSessionState, otherTopics := emptyMsgChan(connectMsgChan, uplink.logEntry)
		if connectErr != nil {  
			if mustAuthenticate != nil && *mustAuthenticate {
				return errors.Errorf("websocket connected, but authentication failed")
			}
			if onConnectedSessionState == nil && len(otherTopics) < 1 {
				return errors.Errorf("websocket connected, but no state created")
			}
			if onConnectedSessionState != nil {
				switch *onConnectedSessionState {
				case constant.OnConnectedSessionCreated, constant.OnConnectedSessionAttached:
					return nil  
				case constant.OnConnectedSessionErrorNoLicense, constant.OnConnectedSessionErrorLicenseReNew, constant.OnConnectedSessionErrorLimitExceeded,
					constant.OnConnectedSessionErrorSecurityHeaderChanged, constant.OnConnectedSessionAccessControlSetupFailure, constant.OnConnectedSessionErrorAppAccessDenied,
					constant.OnConnectedSessionErrorAppFailure:  
					return errors.Errorf("error connecting to engine: %s", *onConnectedSessionState)
				default:
					uplink.logEntry.Logf(logger.WarningLevel, "unknown engine session state: %s", *onConnectedSessionState)
				}
			}
			if len(otherTopics) > 0 {
				return errors.Errorf("websocket connected, but received error topic/-s: %s", strings.Join(otherTopics, ","))
			}
			return errors.Errorf("websocket connected, but got error on requesting version: %v", connectErr)
		}
	}
	return nil
}
func emptyMsgChan(msgChan chan enigma.SessionMessage, logEntry *logger.LogEntry) (*bool, *string, []string) {
	var mustAuthenticate *bool = nil
	var onConnectedSessionState *string = nil
	otherTopics := make([]string, 0, 1)
	for {
		select {
		case event, ok := <-msgChan:
			if !ok {
				return mustAuthenticate, onConnectedSessionState, otherTopics
			}
			switch event.Topic {
			case constant.EventTopicOnConnected:
				var onConnected OnConnected
				if err := jsonit.Unmarshal(event.Content, &onConnected); err != nil {
					logEntry.Log(logger.WarningLevel, "failed to unmarshal pushed onConnected message")
					return mustAuthenticate, onConnectedSessionState, otherTopics
				}
				logEntry.LogInfo("OnConnected", string(event.Content))
				onConnectedSessionState = &onConnected.SessionState
			case constant.EventTopicOnAuthenticationInformation:
				mustAuthenticate = handleOnAuthenticationInformation(event.Content, logEntry)
			default:
				otherTopics = append(otherTopics, event.Topic)
			}
		default:  
			return mustAuthenticate, onConnectedSessionState, otherTopics
		}
	}
}
func handleOnAuthenticationInformation(content json.RawMessage, logEntry *logger.LogEntry) *bool {
	var onAuthInfo OnAuthenticationInformation
	if err := jsonit.Unmarshal(content, &onAuthInfo); err != nil {
		logEntry.Log(logger.WarningLevel, "failed to unmarshal pushed OnAuthenticationInformation message")
		return nil
	}
	return &onAuthInfo.MustAuthenticate
}
func (uplink *SenseUplink) Disconnect() {
	uplink.cancel()
	if uplink.Global != nil {
		uplink.Global.DisconnectFromServer()
	}
}