func (rc *RcpClient) HandleReadCommand(req sdkModels.CommandRequest) (*sdkModels.CommandValue, error) {
	var cv *sdkModels.CommandValue
	var err error
	if alarmType, ok := req.Attributes["alarm_type"].(string); ok {
		alarmType, err := strconv.Atoi(alarmType)
		if err != nil {
			return nil, err
		}
		data := rc.getAlarmState(alarmType)
		cv, err = sdkModels.NewCommandValue(req.DeviceResourceName, common.ValueTypeBool, data)
		if err != nil {
			return nil, err
		}
	} else if counterType, ok := req.Attributes["counter_name"].(string); ok {
		data := rc.getCounterState(counterType)
		cv, err = sdkModels.NewCommandValue(req.DeviceResourceName, common.ValueTypeUint32, uint32(data))
		if err != nil {
			return nil, err
		}
	} else {
		return nil, fmt.Errorf("rcp: unrecognized read command")
	}
	return cv, nil
}
HandleReadCommand handles requests to read data from the device via the RCP api
package bosch
import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"
	"unicode/utf16"
	sdkModels "github.com/edgexfoundry/device-sdk-go/v2/pkg/models"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/common"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/models"
	"github.com/edgexfoundry/device-camera-go/internal/pkg/client"
	"github.com/edgexfoundry/device-camera-go/internal/pkg/digest"
)
const (
	confAlarmOverview    = "0x0c38"
	confIvaCounterValues = "0x0b4a"
	alarmTypeUnknown                   = 0
	alarmTypeVca                       = 1
	alarmTypeRelais                    = 2
	alarmTypeDigitalIntput             = 3
	alarmTypeAudio                     = 4
	alarmTypeVirtualInput              = 5
	alarmTypeDefaultTask               = 16
	alarmTypeGlobalChange              = 17
	alarmTypeSignalTooBright           = 18
	alarmTypeSignalTooDark             = 19
	alarmTypeReferenceImageCheckFailed = 23
	alarmTypeInvalidConfiguration      = 24
	alarmTypeFlameDetected             = 25
	alarmTypeSmokeDetected             = 26
	alarmTypeObjectInField             = 32
	alarmTypeCrossingLine              = 33
	alarmTypeLoitering                 = 34
	alarmTypeConditionChange           = 35
	alarmTypeFollowingRoute            = 36
	alarmTypeTampering                 = 37
	alarmTypeRemovedObject             = 38
	alarmTypeIdleObject                = 39
	alarmTypeEnteringField             = 40
	alarmTypeLeavingField              = 41
	alarmTypeSimilaritySearch          = 42
	alarmTypeCrowdDetection            = 43
	alarmTypeFlowInField               = 44
	alarmTypeCounterFlowInField        = 45
	alarmTypeMotionInField             = 46
	alarmTypeManOverboard              = 47
	alarmTypeCounter                   = 48
	alarmTypeBevPeopleCounter          = 49
	alarmTypeOccupancy                 = 50
	alarmAddFlag      = 0x80
	alarmDeleteFlag   = 0x40
	alarmStateFlag    = 0x20
	alarmStateSetFlag = 0x10
	rcpFmtURL = "http://%s/rcp.xml?%s=%s"
)
type alarm struct {
	EntryID      uint16
	EntryLength  uint16
	FlagAdd      bool
	FlagDelete   bool
	FlagState    bool
	FlagStateSet bool
	AlarmSource  uint16
	AlarmType    uint16
	AlarmName    string
}
type counterData struct {
	ID    uint8
	Type  uint8
	Name  string
	Value uint32
}
type msgList struct {
	XMLName xml.Name `xml:"message_list"`
	Msgs    []msg    `xml:"msg"`
}
type msg struct {
	Command string `xml:"command"`
	Num     string `xml:"num"`
	Cltid   string `xml:"cltid"`
	Hex     string `xml:"hex"`
}
func getXML(dc digest.Client, url string) ([]byte, error) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	resp, err := dc.Do(req)
	if err != nil {
		return nil, fmt.Errorf("GET Error: %v", err.Error())
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("status Error: %v", resp.StatusCode)
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read Error: %v", err.Error())
	}
	return data, nil
}
type packet struct {
	buffer []byte
}
func (p *packet) byte(i int) uint8 {
	return uint8(binary.BigEndian.Uint32([]byte{0, 0, 0, p.buffer[i]}))
}
func (p *packet) uint16(i int) uint16 {
	return binary.BigEndian.Uint16(p.buffer[i : i+2])
}
func (p *packet) uint32(i int) uint32 {
	return binary.BigEndian.Uint32(p.buffer[i : i+4])
}
func (p *packet) utf16string(i int, n int) string {
	ints := make([]uint16, n/2)
	if err := binary.Read(bytes.NewReader(p.buffer[i:i+n]), binary.BigEndian, &ints); err != nil {
		return ""
	}
	return string(utf16.Decode(ints))
}
func parseAlarms(bytes []byte) (alarms []alarm) {
	packet := packet{buffer: bytes}
	readout := (packet.byte(0) & 0x80) != 0
	for i := 4; i < len(packet.buffer); {
		var alarm alarm
		alarm.EntryID = packet.uint16(i)
		alarm.EntryLength = packet.uint16(i + 2)
		flags := packet.byte(i + 4)
		alarm.FlagAdd = (flags & alarmAddFlag) != 0
		alarm.FlagDelete = (flags & alarmDeleteFlag) != 0
		alarm.FlagState = (flags & alarmStateFlag) != 0
		alarm.FlagStateSet = (flags & alarmStateSetFlag) != 0
		alarm.AlarmSource = uint16(packet.byte(i + 6))
		alarm.AlarmType = uint16(packet.byte(i + 7))
		alarm.AlarmName = packet.utf16string(i+8, int(alarm.EntryLength-8))
		i = i + int(alarm.EntryLength)
		if !readout || alarm.FlagState {
			alarms = append(alarms, alarm)
		}
	}
	return
}
func parseCounters(bytes []byte) (counters []counterData) {
	packet := packet{buffer: bytes[1:]}
	for i := 0; i < len(packet.buffer); {
		var counter counterData
		counter.ID = packet.byte(i)
		counter.Type = packet.byte(i + 1)
		counter.Name = packet.utf16string(i+2, 64)
		counter.Value = packet.uint32(i + 66)
		i = i + 70
		counters = append(counters, counter)
	}
	return
}
type RcpClient struct {
	client    digest.Client
	asyncChan chan<- *sdkModels.AsyncValues
	lc        logger.LoggingClient
	alarms   map[int]models.DeviceResource
	counters map[string]models.DeviceResource
	alarmStates   map[int]bool
	counterStates map[string]int
	stop    chan bool
	stopped chan bool
}
func NewClient(asyncCh chan<- *sdkModels.AsyncValues, lc logger.LoggingClient) client.Client {
	return &RcpClient{asyncChan: asyncCh, lc: lc}
}
func (rc *RcpClient) CameraRelease(force bool) {
	close(rc.stop)
	if !force {
		<-rc.stopped
	}
}
func (rc *RcpClient) CameraInit(edgexDevice models.Device, edgexProfile models.DeviceProfile, ipAddress string, username string, password string) {
	if rc.client == nil {
		rc.initializeDClient(username, password)
	}
	if rc.alarms == nil {
		rc.alarms = make(map[int]models.DeviceResource)
	}
	if rc.counters == nil {
		rc.counters = make(map[string]models.DeviceResource)
	}
	if rc.alarmStates == nil {
		rc.alarmStates = make(map[int]bool)
	}
	if rc.counterStates == nil {
		rc.counterStates = make(map[string]int)
	}
	stopchan := make(chan bool)
	stoppedchan := make(chan bool)
	defer close(stoppedchan)
	deviceResources := edgexProfile.DeviceResources
	for _, e := range deviceResources {
		alarmType, ok := e.Attributes["alarm_type"].(string)
		if ok {
			val, err := strconv.Atoi(alarmType)
			if err == nil {
				rc.alarms[val] = e
			}
			continue
		}
		counterName := e.Attributes["counter_name"].(string)
		if counterName != "" {
			rc.counters[counterName] = e
		}
	}
	go func() {
		ticks := time.Tick(time.Second * 5)
		var maxErrors = 60
		for maxErrors > 0 {
			select {
			case <-ticks:
				err := rc.requestEvents(edgexDevice, ipAddress, stopchan)
				if err != nil {
					rc.lc.Error(fmt.Sprintf("Error in RCP loop: %s", err.Error()))
					maxErrors--
				} else {
					maxErrors = 60
				}
			case <-stopchan:
				return
			}
		}
	}()
	rc.stop = stopchan
	rc.stopped = stoppedchan
}
func (rc *RcpClient) initializeDClient(username string, password string) {
	rc.client = digest.NewDClient(&http.Client{}, username, password)
}
func (rc *RcpClient) setAlarmState(alarmType int, state bool) {
	rc.alarmStates[alarmType] = state
}
func (rc *RcpClient) getAlarmState(alarmType int) bool {
	return rc.alarmStates[alarmType]
}
func (rc *RcpClient) setCounterState(counter string, count int) {
	rc.counterStates[counter] = count
}
func (rc *RcpClient) getCounterState(counter string) int {
	return rc.counterStates[counter]
}