feat: send car configuration at startime
This commit is contained in:
		| @@ -6,6 +6,7 @@ import ( | ||||
| 	events2 "github.com/cyrilix/robocar-protobuf/go/events" | ||||
| 	"github.com/cyrilix/robocar-simulator/pkg/events" | ||||
| 	"github.com/cyrilix/robocar-simulator/pkg/gateway" | ||||
| 	"github.com/cyrilix/robocar-simulator/pkg/simulator" | ||||
| 	mqtt "github.com/eclipse/paho.mqtt.golang" | ||||
| 	"github.com/golang/protobuf/proto" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| @@ -48,7 +49,15 @@ func main() { | ||||
| 	} | ||||
| 	defer client.Disconnect(10) | ||||
|  | ||||
| 	gtw := gateway.New(address) | ||||
| 	carConfig := simulator.CarConfigMsg{ | ||||
| 		MsgType:   simulator.MsgTypeCarConfig, | ||||
| 		BodyStyle: simulator.CarConfigBodyStyleDonkey, | ||||
| 		BodyR:     "0", | ||||
| 		BodyG:     "0", | ||||
| 		BodyB:     "255", | ||||
| 		FontSize:  "0", | ||||
| 	} | ||||
| 	gtw := gateway.New(address, &carConfig) | ||||
| 	defer gtw.Stop() | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -18,7 +18,7 @@ func TestGateway_WriteSteering(t *testing.T) { | ||||
| 			&events.SteeringMessage{Steering: 0.5, Confidence: 1}, | ||||
| 			nil, | ||||
| 			simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.50", | ||||
| 				Throttle: "0.0", | ||||
| 				Brake:    "0.0", | ||||
| @@ -26,13 +26,13 @@ func TestGateway_WriteSteering(t *testing.T) { | ||||
| 		{"Update steering", | ||||
| 			&events.SteeringMessage{Steering: -0.5, Confidence: 1}, | ||||
| 			&simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.0", | ||||
| 				Brake:    "0.0", | ||||
| 			}, | ||||
| 			simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "-0.50", | ||||
| 				Throttle: "0.0", | ||||
| 				Brake:    "0.0", | ||||
| @@ -40,13 +40,13 @@ func TestGateway_WriteSteering(t *testing.T) { | ||||
| 		{"Update steering shouldn't erase throttle value", | ||||
| 			&events.SteeringMessage{Steering: -0.3, Confidence: 1}, | ||||
| 			&simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.6", | ||||
| 				Brake:    "0.1", | ||||
| 			}, | ||||
| 			simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "-0.30", | ||||
| 				Throttle: "0.6", | ||||
| 				Brake:    "0.1", | ||||
| @@ -95,7 +95,7 @@ func TestGateway_WriteThrottle(t *testing.T) { | ||||
| 			&events.ThrottleMessage{Throttle: 0.5, Confidence: 1}, | ||||
| 			nil, | ||||
| 			simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.0", | ||||
| 				Throttle: "0.50", | ||||
| 				Brake:    "0.0", | ||||
| @@ -103,13 +103,13 @@ func TestGateway_WriteThrottle(t *testing.T) { | ||||
| 		{"Update Throttle", | ||||
| 			&events.ThrottleMessage{Throttle: 0.6, Confidence: 1}, | ||||
| 			&simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0", | ||||
| 				Throttle: "0.4", | ||||
| 				Brake:    "0", | ||||
| 			}, | ||||
| 			simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0", | ||||
| 				Throttle: "0.60", | ||||
| 				Brake:    "0.0", | ||||
| @@ -117,13 +117,13 @@ func TestGateway_WriteThrottle(t *testing.T) { | ||||
| 		{"Update steering shouldn't erase throttle value", | ||||
| 			&events.ThrottleMessage{Throttle: 0.3, Confidence: 1}, | ||||
| 			&simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.6", | ||||
| 				Brake:    "0.0", | ||||
| 			}, | ||||
| 			simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.30", | ||||
| 				Brake:    "0.0", | ||||
| @@ -131,13 +131,13 @@ func TestGateway_WriteThrottle(t *testing.T) { | ||||
| 		{"Throttle to brake", | ||||
| 			&events.ThrottleMessage{Throttle: -0.7, Confidence: 1}, | ||||
| 			&simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.6", | ||||
| 				Brake:    "0.0", | ||||
| 			}, | ||||
| 			simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.0", | ||||
| 				Brake:    "0.70", | ||||
| @@ -145,13 +145,13 @@ func TestGateway_WriteThrottle(t *testing.T) { | ||||
| 		{"Update brake", | ||||
| 			&events.ThrottleMessage{Throttle: -0.2, Confidence: 1}, | ||||
| 			&simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.0", | ||||
| 				Brake:    "0.5", | ||||
| 			}, | ||||
| 			simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.0", | ||||
| 				Brake:    "0.20", | ||||
| @@ -159,13 +159,13 @@ func TestGateway_WriteThrottle(t *testing.T) { | ||||
| 		{"Brake to throttle", | ||||
| 			&events.ThrottleMessage{Throttle: 0.9, Confidence: 1}, | ||||
| 			&simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.0", | ||||
| 				Brake:    "0.4", | ||||
| 			}, | ||||
| 			simulator.ControlMsg{ | ||||
| 				MsgType:  "control", | ||||
| 				MsgType: simulator.MsgTypeControl, | ||||
| 				Steering: "0.2", | ||||
| 				Throttle: "0.90", | ||||
| 				Brake:    "0.0", | ||||
|   | ||||
| @@ -31,7 +31,7 @@ type ThrottleSource interface { | ||||
| 	SubscribeThrottle() <-chan *events.ThrottleMessage | ||||
| } | ||||
|  | ||||
| func New(addressSimulator string) *Gateway { | ||||
| func New(addressSimulator string, car *simulator.CarConfigMsg) *Gateway { | ||||
| 	l := log.WithField("simulator", addressSimulator) | ||||
| 	l.Info("run gateway from simulator") | ||||
|  | ||||
| @@ -41,6 +41,9 @@ func New(addressSimulator string) *Gateway { | ||||
| 		frameSubscribers:    make(map[chan<- *events.FrameMessage]interface{}), | ||||
| 		steeringSubscribers: make(map[chan<- *events.SteeringMessage]interface{}), | ||||
| 		throttleSubscribers: make(map[chan<- *events.ThrottleMessage]interface{}), | ||||
| 		telemetrySubscribers: make(map[chan *simulator.TelemetryMsg]interface{}), | ||||
| 		carSubscribers: make(map[chan *simulator.Msg]interface{}), | ||||
| 		carConfig:           car, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -49,6 +52,8 @@ type Gateway struct { | ||||
| 	cancel chan interface{} | ||||
|  | ||||
| 	address string | ||||
|  | ||||
| 	muCommand sync.Mutex | ||||
| 	muConn  sync.Mutex | ||||
| 	conn    io.ReadWriteCloser | ||||
|  | ||||
| @@ -60,14 +65,24 @@ type Gateway struct { | ||||
| 	frameSubscribers    map[chan<- *events.FrameMessage]interface{} | ||||
| 	steeringSubscribers map[chan<- *events.SteeringMessage]interface{} | ||||
| 	throttleSubscribers map[chan<- *events.ThrottleMessage]interface{} | ||||
|  | ||||
| 	telemetrySubscribers map[chan *simulator.TelemetryMsg]interface{} | ||||
| 	carSubscribers map[chan *simulator.Msg]interface{} | ||||
|  | ||||
| 	carConfig *simulator.CarConfigMsg | ||||
| } | ||||
|  | ||||
| func (g *Gateway) Start() error { | ||||
| 	g.log.Info("connect to simulator") | ||||
| 	g.cancel = make(chan interface{}) | ||||
| 	msgChan := make(chan *simulator.TelemetryMsg) | ||||
| 	msgChan := g.subscribeTelemetryEvents() | ||||
|  | ||||
| 	go g.run(msgChan) | ||||
| 	go g.run() | ||||
|  | ||||
| 	err := g.writeCarConfig() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to configure car to server: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	for { | ||||
| 		select { | ||||
| @@ -100,6 +115,12 @@ func (g *Gateway) Close() error { | ||||
| 	for c := range g.throttleSubscribers { | ||||
| 		close(c) | ||||
| 	} | ||||
| 	for c := range g.telemetrySubscribers { | ||||
| 		close(c) | ||||
| 	} | ||||
| 	for c := range g.carSubscribers { | ||||
| 		close(c) | ||||
| 	} | ||||
| 	if g.conn == nil { | ||||
| 		g.log.Warn("no connection to close") | ||||
| 		return nil | ||||
| @@ -110,16 +131,14 @@ func (g *Gateway) Close() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (g *Gateway) run(msgChan chan<- *simulator.TelemetryMsg) { | ||||
| func (g *Gateway) run() { | ||||
| 	err := g.connect() | ||||
| 	if err != nil { | ||||
| 		g.log.Panicf("unable to connect to simulator: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	reader := bufio.NewReader(g.conn) | ||||
|  | ||||
| 	err = retry.Do( | ||||
| 		func() error { return g.listen(msgChan, reader) }, | ||||
| 		func() error { return g.listen() }, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		g.log.Errorf("unable to connect to server: %v", err) | ||||
| @@ -149,7 +168,9 @@ func (g *Gateway) connect() error { | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (g *Gateway) listen(msgChan chan<- *simulator.TelemetryMsg, reader *bufio.Reader) error { | ||||
| func (g *Gateway) listen() error { | ||||
| 	reader := bufio.NewReader(g.conn) | ||||
|  | ||||
| 	for { | ||||
| 		rawLine, err := reader.ReadBytes('\n') | ||||
| 		if err == io.EOF { | ||||
| @@ -160,15 +181,43 @@ func (g *Gateway) listen(msgChan chan<- *simulator.TelemetryMsg, reader *bufio.R | ||||
| 			return fmt.Errorf("unable to read response: %v", err) | ||||
| 		} | ||||
|  | ||||
| 		var msg simulator.TelemetryMsg | ||||
| 		var msg simulator.Msg | ||||
| 		err = json.Unmarshal(rawLine, &msg) | ||||
| 		if err != nil { | ||||
| 			g.log.Errorf("unable to unmarshal simulator msg '%v': %v", string(rawLine), err) | ||||
| 		} | ||||
| 		if "telemetry" != msg.MsgType { | ||||
| 			continue | ||||
|  | ||||
| 		switch msg.MsgType { | ||||
| 		case simulator.MsgTypeTelemetry: | ||||
| 			g.broadcastTelemetryMsg(rawLine) | ||||
| 		case simulator.MsgTypeCarLoaded: | ||||
| 			g.broadcastCarMsg(rawLine) | ||||
| 		default: | ||||
| 			log.Warnf("unmanaged simulator message: %v", rawLine) | ||||
| 		} | ||||
| 		msgChan <- &msg | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (g *Gateway) broadcastTelemetryMsg(rawLine []byte) { | ||||
| 	for c := range g.telemetrySubscribers { | ||||
|  | ||||
| 		var tMsg simulator.TelemetryMsg | ||||
| 		err := json.Unmarshal(rawLine, &tMsg) | ||||
| 		if err != nil { | ||||
| 			g.log.Errorf("unable to unmarshal telemetry simulator msg '%v': %v", string(rawLine), err) | ||||
| 		} | ||||
| 		c <- &tMsg | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (g *Gateway) broadcastCarMsg(rawLine []byte) { | ||||
| 	for c := range g.carSubscribers { | ||||
| 		var tMsg simulator.Msg | ||||
| 		err := json.Unmarshal(rawLine, &tMsg) | ||||
| 		if err != nil { | ||||
| 			g.log.Errorf("unable to unmarshal car simulator msg '%v': %v", string(rawLine), err) | ||||
| 		} | ||||
| 		c <- &tMsg | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -240,6 +289,25 @@ func (g *Gateway) SubscribeThrottle() <-chan *events.ThrottleMessage { | ||||
| 	return throttleChan | ||||
| } | ||||
|  | ||||
| func (g *Gateway) subscribeTelemetryEvents() chan *simulator.TelemetryMsg { | ||||
| 	telemetryChan := make(chan *simulator.TelemetryMsg) | ||||
| 	g.telemetrySubscribers[telemetryChan] = struct{}{} | ||||
| 	return telemetryChan | ||||
| } | ||||
| func (g *Gateway) unsubscribeTelemetryEvents(telemetryChan chan *simulator.TelemetryMsg) { | ||||
| 	delete(g.telemetrySubscribers, telemetryChan) | ||||
| 	close(telemetryChan) | ||||
| } | ||||
| func (g *Gateway) subscribeCarEvents() chan *simulator.Msg { | ||||
| 	carChan := make(chan *simulator.Msg) | ||||
| 	g.carSubscribers[carChan] = struct{}{} | ||||
| 	return carChan | ||||
| } | ||||
| func (g *Gateway) unsubscribeCarEvents(carChan chan *simulator.Msg) { | ||||
| 	delete(g.carSubscribers, carChan) | ||||
| 	close(carChan) | ||||
| } | ||||
|  | ||||
| var connect = func(address string) (io.ReadWriteCloser, error) { | ||||
| 	conn, err := net.Dial("tcp", address) | ||||
| 	if err != nil { | ||||
| @@ -258,28 +326,38 @@ func (g *Gateway) WriteSteering(message *events.SteeringMessage) { | ||||
| } | ||||
|  | ||||
| func (g *Gateway) writeControlCommandToSimulator() { | ||||
| 	if err := g.connect(); err != nil { | ||||
| 		g.log.Errorf("unable to connect to simulator to send control command: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 	log.Debugf("write command to simulator: %v", g.lastControl) | ||||
| 	w := bufio.NewWriter(g.conn) | ||||
| 	content, err := json.Marshal(g.lastControl) | ||||
| 	if err != nil { | ||||
| 		g.log.Errorf("unable to marshall control msg \"%#v\": %v", g.lastControl, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	_, err = w.Write(append(content, '\n')) | ||||
| 	err = g.writeCommand(content) | ||||
| 	if err != nil { | ||||
| 		g.log.Errorf("unable to write control msg \"%#v\" to simulator: %v", g.lastControl, err) | ||||
| 		return | ||||
| 		g.log.Errorf("unable to send control command to simulator: %v", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (g *Gateway) writeCommand(content []byte) error { | ||||
| 	g.muCommand.Lock() | ||||
| 	defer g.muCommand.Unlock() | ||||
|  | ||||
| 	if err := g.connect(); err != nil { | ||||
| 		g.log.Errorf("unable to connect to simulator to send control command: %v", err) | ||||
| 		return nil | ||||
| 	} | ||||
| 	log.Debugf("write command to simulator: %v", g.lastControl) | ||||
| 	w := bufio.NewWriter(g.conn) | ||||
|  | ||||
| 	_, err := w.Write(append(content, '\n')) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to write control msg \"%#v\" to simulator: %v", g.lastControl, err) | ||||
| 	} | ||||
| 	err = w.Flush() | ||||
| 	if err != nil { | ||||
| 		g.log.Errorf("unable to flush control msg \"%#v\" to simulator: %v", g.lastControl, err) | ||||
| 		return | ||||
| 		return fmt.Errorf("unable to flush control msg \"%#v\" to simulator: %v", g.lastControl, err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (g *Gateway) WriteThrottle(message *events.ThrottleMessage) { | ||||
| @@ -292,7 +370,7 @@ func (g *Gateway) WriteThrottle(message *events.ThrottleMessage) { | ||||
| 		g.lastControl.Brake = "0.0" | ||||
| 	} else { | ||||
| 		g.lastControl.Throttle = "0.0" | ||||
| 		g.lastControl.Brake = fmt.Sprintf("%.2f", -1 * message.Throttle) | ||||
| 		g.lastControl.Brake = fmt.Sprintf("%.2f", -1*message.Throttle) | ||||
| 	} | ||||
|  | ||||
| 	g.writeControlCommandToSimulator() | ||||
| @@ -303,9 +381,30 @@ func (g *Gateway) initLastControlMsg() { | ||||
| 		return | ||||
| 	} | ||||
| 	g.lastControl = &simulator.ControlMsg{ | ||||
| 		MsgType:  "control", | ||||
| 		MsgType:  simulator.MsgTypeControl, | ||||
| 		Steering: "0.0", | ||||
| 		Throttle: "0.0", | ||||
| 		Brake:    "0.0", | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (g *Gateway) writeCarConfig() error { | ||||
| 	carChan := g.subscribeCarEvents() | ||||
| 	defer g.unsubscribeCarEvents(carChan) | ||||
|  | ||||
| 	g.log.Info("Send car configuration") | ||||
|  | ||||
| 	content, err := json.Marshal(g.carConfig) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to marshall car config msg \"%#v\": %v", g.lastControl, err) | ||||
| 	} | ||||
|  | ||||
| 	err = g.writeCommand(content) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to send car config to simulator: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	msg := <- carChan | ||||
| 	g.log.Infof("Car loaded: %v", msg) | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,16 @@ | ||||
| package simulator | ||||
|  | ||||
| const( | ||||
| 	MsgTypeControl =  "control" | ||||
| 	MsgTypeTelemetry =  "telemetry" | ||||
| 	MsgTypeCarConfig =  "car_config" | ||||
| 	MsgTypeCarLoaded =  "car_loaded" | ||||
| ) | ||||
|  | ||||
| type Msg struct { | ||||
| 	MsgType string `json:"msg_type"` | ||||
| } | ||||
|  | ||||
| type TelemetryMsg struct { | ||||
| 	MsgType string `json:"msg_type"` | ||||
| 	SteeringAngle float64 `json:"steering_angle"` | ||||
| @@ -17,7 +28,74 @@ type TelemetryMsg struct { | ||||
| /* Json msg used to control cars. MsgType must be filled with "control" */ | ||||
| type ControlMsg struct { | ||||
| 	MsgType string `json:"msg_type"` | ||||
| 	Steering float32 `json:"steering"` | ||||
| 	Throttle float32 `json:"throttle"` | ||||
| 	Brake    float32 `json:"brake"` | ||||
| 	Steering string `json:"steering"` | ||||
| 	Throttle string `json:"throttle"` | ||||
| 	Brake    string `json:"brake"` | ||||
| } | ||||
|  | ||||
| type GetSceneNamesMsg struct { | ||||
| 	MsgType string `json:"msg_type"` | ||||
| 	SceneName string `json:"scene_name"` | ||||
| } | ||||
|  | ||||
| type LoadSceneMsg struct { | ||||
| 	MsgType string `json:"msg_type"` | ||||
| 	SceneName string `json:"scene_name"` | ||||
| } | ||||
|  | ||||
| const( | ||||
| 	CarConfigBodyStyleDonkey = "donkey" | ||||
| 	CarConfigBodyStyleBare = "bare" | ||||
| 	CarConfigBodyStyleCar01 = "car01" | ||||
| ) | ||||
| /* | ||||
| 	# body_style = "donkey" | "bare" | "car01" choice of string | ||||
| 	# body_rgb  = (128, 128, 128) tuple of ints | ||||
| 	# car_name = "string less than 64 char" | ||||
| */ | ||||
| type CarConfigMsg struct { | ||||
| 	MsgType string `json:"msg_type"` | ||||
| 	BodyStyle string `json:"body_style"` | ||||
| 	BodyR     string `json:"body_r"` | ||||
| 	BodyG     string `json:"body_g"` | ||||
| 	BodyB     string `json:"body_b"` | ||||
| 	CarName   string `json:"car_name"` | ||||
| 	FontSize  string `json:"font_size"` | ||||
| } | ||||
|  | ||||
| /* | ||||
| # car_name = "string less than 64 char" | ||||
| # guid = "some random string" | ||||
| */ | ||||
| type RacerBioMsg struct { | ||||
| 	MsgType string `json:"msg_type"` | ||||
| 	RacerName string `json:"racer_name"` | ||||
| 	CarName   string `json:"car_name"` | ||||
| 	Bio       string `json:"bio"` | ||||
| 	Country   string `json:"country"` | ||||
| 	Guid      string `json:"guid"` | ||||
| } | ||||
|  | ||||
| /* Camera config | ||||
| set any field to Zero to get the default camera setting. | ||||
| offset_x moves camera left/right | ||||
| offset_y moves camera up/down | ||||
| offset_z moves camera forward/back | ||||
| rot_x will rotate the camera | ||||
| with fish_eye_x/y == 0.0 then you get no distortion | ||||
| img_enc can be one of JPG|PNG|TGA | ||||
| */ | ||||
| type CamConfigMsg struct { | ||||
| 	MsgType string `json:"msg_type"` | ||||
| 	Fov      string `json:"fov"` | ||||
| 	FishEyeX string `json:"fish_eye_x"` | ||||
| 	FishEyeY string `json:"fish_eye_Y"` | ||||
| 	ImgW     string `json:"img_w"` | ||||
| 	ImgH     string `json:"img_h"` | ||||
| 	ImgD     string `json:"img_d"` | ||||
| 	ImgEnc   string `json:"img_enc"` | ||||
| 	OffsetX  string `json:"offset_x"` | ||||
| 	OffsetY  string `json:"offset_y"` | ||||
| 	OffsetZ  string `json:"offset_z"` | ||||
| 	RotX     string `json:"rot_x"` | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user