func logNotification(env *task.Env, task *task.MessageTask, pdu *libcoap.Pdu) {
    log.Infof("Message Code: %v (%+v)", pdu.Code, pdu.CoapCode())
	if pdu.Data == nil {
		return
    }
    var err error
    var logStr string
    var req *libcoap.Pdu
    if task != nil {
        req = task.GetMessage()
    } else {
        req = nil
    }
    observe, err := pdu.GetOptionIntegerValue(libcoap.OptionObserve)
    if err != nil {
        log.WithError(err).Warn("Get observe option value failed.")
        return
    }
    log.WithField("Observe Value:", observe).Info("Notification Message")
	maxAgeRes := pdu.GetOptionStringValue(libcoap.OptionMaxage)
	if maxAgeRes != "" {
		log.Infof("Max-Age Option: %v", maxAgeRes)
	}
    log.Infof("        Raw payload: %s", pdu.Data)
    hex := hex.Dump(pdu.Data)
	log.Infof("        Raw payload hex: \n%s", hex)
	if pdu.IsMessageResponse() {
		log.Debugf("Server send notification with error message: %+v", pdu.Data)
		return
	}
	dec := codec.NewDecoder(bytes.NewReader(pdu.Data), dots_common.NewCborHandle())
    if strings.Contains(hex, string(libcoap.IETF_MITIGATION_SCOPE_HEX)) {
        var v messages.MitigationResponse
        err = dec.Decode(&v)
        logStr = v.String()
        env.UpdateCountMitigation(req, v, string(pdu.Token))
        log.Debugf("Request query with token as key in map: %+v", env.GetAllRequestQuery())
    } else if strings.Contains(hex, string(libcoap.IETF_SESSION_CONFIGURATION_HEX)) {
        var v messages.ConfigurationResponse
        err = dec.Decode(&v)
        logStr = v.String()
        log.Debug("Receive session notification - Client update new values to system session configuration and restart ping task.")
		RestartHeartBeatTask(pdu, env)
		sessionTask := env.SessionConfigTask()
		if sessionTask != nil {
			RefreshSessionConfig(pdu, env, sessionTask.MessageTask())
		}
	} else if strings.Contains(hex, string(libcoap.IETF_TELEMETRY_PRE_MITIGATION)) {
        var v messages.TelemetryPreMitigationResponse
        err = dec.Decode(&v)
        logStr = v.String()
        log.Debug("Receive telemetry pre-mitigation notification.")
    }else {
        log.Warnf("Unknown notification is received.")
    }
    if err != nil {
        log.WithError(err).Warn("CBOR Decode failed.")
        return
    }
    log.Infof("        CBOR decoded: %s", logStr)
}
/*
Print log of notification when observe the mitigation
parameter:
pdu   response pdu notification
task  the request task for blockwise transfer process
env   the client environment data
package main
import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"
	"strconv"
	"github.com/ugorji/go/codec"
	"github.com/nttdots/go-dots/dots_common"
	"github.com/nttdots/go-dots/dots_common/messages"
	"github.com/nttdots/go-dots/dots_client/task"
	"github.com/nttdots/go-dots/libcoap"
	"github.com/shopspring/decimal"
	log "github.com/sirupsen/logrus"
	dots_config "github.com/nttdots/go-dots/dots_client/config"
	client_message "github.com/nttdots/go-dots/dots_client/messages"
)
type RequestInterface interface {
	LoadJson([]byte) error
	CreateRequest()
	Send() Response
}
type Request struct {
	Message     interface{}
	RequestCode messages.Code
	pdu         *libcoap.Pdu
	coapType    libcoap.Type
	method      string
	requestName string
	queryParams []string
	env         *task.Env
	options     map[messages.Option]string
}
type Response struct {
	StatusCode libcoap.Code
	data       []byte
}
func NewRequest(code messages.Code, coapType libcoap.Type, method string, requestName string, queryParams []string, env *task.Env, options map[messages.Option]string) *Request {
	return &Request{
		nil,
		code,
		nil,
		coapType,
		method,
		requestName,
		queryParams,
		env,
		options,
	}
}
func (r *Request) LoadMessage(message interface{}) {
	r.Message = message
}
func (r *Request) dumpCbor() []byte {
	var buf []byte
	e := codec.NewEncoderBytes(&buf, dots_common.NewCborHandle())
	err := e.Encode(r.Message)
	if err != nil {
		log.Errorf("Error decoding %s", err)
	}
	return buf
}
func (r *Request) dumpJson() []byte {
	payload, _ := json.Marshal(r.Message)
	return payload
}
func (r *Request) LoadJson(jsonData []byte) error {
	m := reflect.New(r.RequestCode.Type()).Interface()
	err := json.Unmarshal(jsonData, &m)
	if err != nil {
		return fmt.Errorf("Can't Convert Json to Message Object: %v\n", err)
	}
	r.Message = m
	return nil
}
func (r *Request) pathString() {
	r.RequestCode.PathString()
}
func (r *Request) CreateRequest() {
	var code libcoap.Code
	var observe uint16
	switch strings.ToUpper(r.method) {
	case "GET":
		code = libcoap.RequestGet
	case "PUT":
		code = libcoap.RequestPut
	case "POST":
		code = libcoap.RequestPost
	case "DELETE":
		code = libcoap.RequestDelete
	default:
		log.WithField("method", r.method).Error("invalid request method.")
	}
	r.pdu = &libcoap.Pdu{}
	r.pdu.Type = r.coapType
	r.pdu.Code = code
	r.pdu.MessageID = r.env.CoapSession().NewMessageID()
	r.pdu.Token = dots_common.RandStringBytes(8)
	r.pdu.Options = make([]libcoap.Option, 0)
	observeStr := r.options[messages.OBSERVE]
	if observeStr != "" {
		observeValue, err := strconv.ParseUint(observeStr, 10, 16)
		if err != nil {
			log.Errorf("Observe is not uint type.")
			goto SKIP_OBSERVE
		}
		observe = uint16(observeValue)
		if observe == uint16(messages.Register) || observe == uint16(messages.Deregister) {
			r.pdu.SetOption(libcoap.OptionObserve, observe)
			queryString := task.QueryParamsToString(r.queryParams)
			token, _ := r.env.GetTokenAndRequestQuery(queryString)
			if observe == uint16(messages.Register) {
				if token != nil {
					r.pdu.Token = token
				} else {
					reqQuery := task.RequestQuery{ queryString, nil }
					r.env.AddRequestQuery(string(r.pdu.Token), &reqQuery)
				}
			} else {
				if token != nil {
					r.pdu.Token = token
					r.env.RemoveRequestQuery(string(token))
				}
			}
		}
	}
SKIP_OBSERVE:
	if val, ok := r.options[messages.IFMATCH]; ok {
		r.pdu.SetOption(libcoap.OptionIfMatch, val)
	}
	if (r.requestName == "mitigation_request") && (r.method == "GET") {
		blockSize := r.env.InitialRequestBlockSize()
		if blockSize != nil {
			block := &libcoap.Block{}
			block.NUM = 0
			block.M   = 0
			block.SZX = *blockSize
			r.pdu.SetOption(libcoap.OptionBlock2, uint32(block.ToInt()))
		} else {
			log.Debugf("Not set block 2 option")
		}
	}
	if r.Message != nil {
		r.pdu.Data = r.dumpCbor()
		r.pdu.SetOption(libcoap.OptionContentFormat, uint16(libcoap.AppDotsCbor))
		log.Debugf("hex dump cbor request:\n%s", hex.Dump(r.pdu.Data))
	}
	requestQueryPaths := strings.Split(r.RequestCode.PathString(), "/")
	requestQueryPaths = append(requestQueryPaths, r.queryParams...)
	r.pdu.SetPath(requestQueryPaths)
	log.Debugf("r.pdu=%+v", r.pdu)
}
func (r *Request) handleResponse(task *task.MessageTask, response *libcoap.Pdu, env *task.Env) {
	isMoreBlock, eTag, block := r.env.CheckBlock(response)
	if isMoreBlock {
		r.pdu.MessageID = r.env.CoapSession().NewMessageID()
		r.pdu.SetOption(libcoap.OptionBlock2, uint32(block.ToInt()))
		r.pdu.SetOption(libcoap.OptionEtag, uint32(*eTag))
		r.options[messages.BLOCK2] = block.ToString()
		task.SetMessage(r.pdu)
		r.env.Run(task)
	} else {
		if eTag != nil && block.NUM > 0 {
			blockKey := strconv.Itoa(*eTag) + string(response.Token)
			response = r.env.GetBlockData(blockKey)
			delete(r.env.Blocks(), blockKey)
		}
		if response.Type == libcoap.TypeNon {
			log.Debugf("Success incoming PDU(HandleResponse): %+v", response)
		}
		if response.Code != 0 {
			task.AddResponse(response)
		}
	}
	log.Debugf("r.queryParam=%v", r.queryParams)
	if (r.requestName == "session_configuration") {
		if (r.method == "GET") && (response.Code == libcoap.ResponseContent) && len(r.queryParams) > 0 {
			log.Debug("Get with sid - Client update new values to system session configuration and restart ping task.")
			RestartHeartBeatTask(response, r.env)
			RefreshSessionConfig(response, r.env, r.pdu)
		} else if (r.method == "PUT") && (response.Code == libcoap.ResponseCreated) {
			log.Debug("The new session configuration has been created. Stop the current session config task")
			RefreshSessionConfig(response, r.env, r.pdu)
		} else if (r.method == "DELETE") && (response.Code == libcoap.ResponseDeleted) {
			log.Debug("The current session configuration has been deleted. Stop the current session config task")
			RefreshSessionConfig(response, r.env, r.pdu)
		}
	}
}
func handleTimeout(task *task.MessageTask, env *task.Env) {
	key := fmt.Sprintf("%x", task.GetMessage().Token)
	delete(env.Requests(), key)
	log.Info("<<< handleTimeout >>>")
}
func heartbeatResponseHandler(_ *task.HeartBeatTask, pdu *libcoap.Pdu) {
	log.WithField("Type", pdu.Type).WithField("Code", pdu.Code).Debug("HeartBeat")
	if pdu.Code != libcoap.ResponseChanged {
		log.Debugf("Error message: %+v", string(pdu.Data))
	} else {
		task.SetIsReceiveResponseContent(true)
	}
}
func heartbeatTimeoutHandler(_ *task.HeartBeatTask, env *task.Env) {
	log.Info("HeartBeat Timeout")
	log.Debug("Exceeded missing_hb_allowed. Stop heartbeat task...")
	env.StopHeartBeat()
	restartConnection(env)
}
func (r *Request) Send() (res Response) {
	var config *dots_config.MessageTaskConfiguration
	if r.pdu.Type == libcoap.TypeNon {
		config = dots_config.GetSystemConfig().NonConfirmableMessageTask
	} else if r.pdu.Type == libcoap.TypeCon {
		config = dots_config.GetSystemConfig().ConfirmableMessageTask
	}
	task := task.NewMessageTask(
		r.pdu,
		time.Duration(config.TaskInterval) * time.Second,
		config.TaskRetryNumber,
		time.Duration(config.TaskTimeout) * time.Second,
		false,
		false,
		r.handleResponse,
		handleTimeout)
	r.env.Run(task)
	pdu := r.env.WaitingForResponse(task)
	data := r.analyzeResponseData(pdu)
	if pdu == nil {
		str := "Request timeout"
		res = Response{ libcoap.ResponseInternalServerError, []byte(str) }
	} else {
		res = Response{ pdu.Code, data }
	}
	return
}
func (r *Request) analyzeResponseData(pdu *libcoap.Pdu) (data []byte) {
	var err error
	var logStr string
	if pdu == nil {
		return
	}
	log.Infof("Message Code: %v (%+v)", pdu.Code, pdu.CoapCode())
	maxAgeRes := pdu.GetOptionStringValue(libcoap.OptionMaxage)
	if maxAgeRes != "" {
		log.Infof("Max-Age Option: %v", maxAgeRes)
	}
	observe, err := pdu.GetOptionIntegerValue(libcoap.OptionObserve)
    if err != nil {
        log.WithError(err).Warn("Get observe option value failed.")
        return
	}
	if observe >= 0 {
		log.WithField("Observe Value:", observe).Info("Notification Message")
	}
	if pdu.Data == nil {
		return
	}
	log.Infof("        Raw payload: %s", pdu.Data)
	log.Infof("        Raw payload hex: \n%s", hex.Dump(pdu.Data))
	if pdu.IsMessageResponse() {
		data = pdu.Data
		return
	}
	dec := codec.NewDecoder(bytes.NewReader(pdu.Data), dots_common.NewCborHandle())
	switch r.requestName {
	case "mitigation_request":
		switch r.method {
		case "GET":
			var v messages.MitigationResponse
			err = dec.Decode(&v)
			if err != nil { goto CBOR_DECODE_FAILED }
			data, err = json.Marshal(v)
			logStr = v.String()
			r.env.SetCountMitigation(v, string(pdu.Token))
			log.Debugf("Request query with token as key in map: %+v", r.env.GetAllRequestQuery())
		case "PUT":
			if pdu.Code == libcoap.ResponseServiceUnavailable {
				var v messages.MitigationResponseServiceUnavailable
				err = dec.Decode(&v)
				if err != nil { goto CBOR_DECODE_FAILED }
				data, err = json.Marshal(v)
				logStr = v.String()
			} else {
				var v messages.MitigationResponsePut
				err = dec.Decode(&v)
				if err != nil { goto CBOR_DECODE_FAILED }
				data, err = json.Marshal(v)
				logStr = v.String()
			}
		default:
			var v messages.MitigationRequest
			err = dec.Decode(&v)
			if err != nil { goto CBOR_DECODE_FAILED }
			data, err = json.Marshal(v)
			logStr = v.String()
		}
	case "session_configuration":
		if r.method == "GET" {
			var v messages.ConfigurationResponse
			err = dec.Decode(&v)
			if err != nil { goto CBOR_DECODE_FAILED }
			data, err = json.Marshal(v)
			logStr = v.String()
		} else {
			var v messages.SignalConfigRequest
			err = dec.Decode(&v)
			if err != nil { goto CBOR_DECODE_FAILED }
			data, err = json.Marshal(v)
			logStr = v.String()
		}
	case "telemetry_setup_request":
		switch r.method {
		case "GET":
			var v messages.TelemetrySetupResponse
			err = dec.Decode(&v)
			if err != nil { goto CBOR_DECODE_FAILED }
			data, err = json.Marshal(v)
			logStr = v.String()
		case "PUT":
			var v messages.TelemetrySetupResponseConflict
			err = dec.Decode(&v)
			if err != nil { goto CBOR_DECODE_FAILED }
			data, err = json.Marshal(v)
			logStr = v.String()
		}
	case "telemetry_pre_mitigation_request":
		switch r.method {
		case "GET":
			var v messages.TelemetryPreMitigationResponse
			err = dec.Decode(&v)
			if err != nil { goto CBOR_DECODE_FAILED }
			data, err = json.Marshal(v)
			logStr = v.String()
		}
	}
	if err != nil {
		log.WithError(err).Warn("Parse object to JSON failed.")
		return
	}
	log.Infof("        CBOR decoded: %s", logStr)
	return
CBOR_DECODE_FAILED:
	log.WithError(err).Warn("CBOR Decode failed.")
	return
}
func RestartHeartBeatTask(pdu *libcoap.Pdu, env *task.Env) {
	if pdu.IsMessageResponse() {
		return
	}
	dec := codec.NewDecoder(bytes.NewReader(pdu.Data), dots_common.NewCborHandle())
	var v messages.ConfigurationResponse
	err := dec.Decode(&v)
	if err != nil {
		log.WithError(err).Warn("CBOR Decode failed.")
		return
	}
	var heartbeatInterval int
	var missingHbAllowed int
	var maxRetransmit int
	var ackTimeout decimal.Decimal
	var ackRandomFactor decimal.Decimal
	if env.SessionConfigMode() == string(client_message.MITIGATING) {
		heartbeatInterval = v.SignalConfigs.MitigatingConfig.HeartbeatInterval.CurrentValue
		missingHbAllowed = v.SignalConfigs.MitigatingConfig.MissingHbAllowed.CurrentValue
		maxRetransmit = v.SignalConfigs.MitigatingConfig.MaxRetransmit.CurrentValue
		ackTimeout = v.SignalConfigs.MitigatingConfig.AckTimeout.CurrentValue.Round(2)
		ackRandomFactor = v.SignalConfigs.MitigatingConfig.AckRandomFactor.CurrentValue.Round(2)
	} else if env.SessionConfigMode() == string(client_message.IDLE) {
		heartbeatInterval = v.SignalConfigs.IdleConfig.HeartbeatInterval.CurrentValue
		missingHbAllowed = v.SignalConfigs.IdleConfig.MissingHbAllowed.CurrentValue
		maxRetransmit = v.SignalConfigs.IdleConfig.MaxRetransmit.CurrentValue
		ackTimeout = v.SignalConfigs.IdleConfig.AckTimeout.CurrentValue.Round(2)
		ackRandomFactor = v.SignalConfigs.IdleConfig.AckRandomFactor.CurrentValue.Round(2)
	}
	log.Debugf("Got session configuration data from server. Restart ping task with heatbeat-interval=%v, missing-hb-allowed=%v...", heartbeatInterval, missingHbAllowed)
	env.SetRetransmitParams(maxRetransmit, ackTimeout, ackRandomFactor)
	pingTimeout, _ := ackTimeout.Float64()
	env.StopHeartBeat()
	env.SetMissingHbAllowed(missingHbAllowed)
	env.Run(task.NewHeartBeatTask(
			time.Duration(heartbeatInterval)* time.Second,
			missingHbAllowed,
			time.Duration(pingTimeout) * time.Second,
			heartbeatResponseHandler,
			heartbeatTimeoutHandler))
}
func RefreshSessionConfig(pdu *libcoap.Pdu, env *task.Env, message *libcoap.Pdu) {
	env.StopSessionConfig()
	maxAgeRes, _ := strconv.Atoi(pdu.GetOptionStringValue(libcoap.OptionMaxage))
	timeFresh := maxAgeRes - env.IntervalBeforeMaxAge()
	if timeFresh > 0 {
		env.Run(task.NewSessionConfigTask(
			message,
			time.Duration(timeFresh) * time.Second,
			sessionConfigResponseHandler,
			sessionConfigTimeoutHandler))
	} else {
		log.Infof("Max-Age Option has value %+v <= %+v value of intervalBeforeMaxAge. Don't refresh session config", maxAgeRes, env.IntervalBeforeMaxAge())
	}
}
func sessionConfigResponseHandler(t *task.SessionConfigTask, pdu *libcoap.Pdu, env *task.Env) {
	log.Infof("Message Code: %v (%+v)", pdu.Code, pdu.CoapCode())
	maxAgeRes, _ := strconv.Atoi(pdu.GetOptionStringValue(libcoap.OptionMaxage))
	log.Infof("Max-Age Option: %v", maxAgeRes)
	log.Infof("        Raw payload: %s", pdu.Data)
	log.Infof("        Raw payload hex: \n%s", hex.Dump(pdu.Data))
	if pdu.IsMessageResponse() {
		return
	}
	dec := codec.NewDecoder(bytes.NewReader(pdu.Data), dots_common.NewCborHandle())
	var v messages.ConfigurationResponse
	err := dec.Decode(&v)
	if err != nil {
		log.WithError(err).Warn("CBOR Decode failed.")
		return
	}
	log.Infof("        CBOR decoded: %+v", v.String())
	if pdu.Code == libcoap.ResponseContent {
		RestartHeartBeatTask(pdu, env)
		RefreshSessionConfig(pdu, env, t.MessageTask())
	}
}
func sessionConfigTimeoutHandler(_ *task.SessionConfigTask, env *task.Env) {
	log.Info("Session config refresh timeout")
	env.StopSessionConfig()
}