Skip to content

Commit

Permalink
auto register data structs with SimConnect#RegisterDataDefinition
Browse files Browse the repository at this point in the history
  • Loading branch information
lian committed Aug 23, 2020
1 parent 6c03cca commit 84f5d13
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.env
build-vfrmap-upload.sh
vfrmap.exe
vfrmap-win64.zip
request_data.exe
_vendor
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# msfs2020-go

simconnect package [msfs2020-go/simconnect](simconnect/) connects to microsoft flight simulator 2020 using golang.

cross-compiles from macos/linux, no other dependencies required. produces a single binary with no other files or configuration required.

## status

[msfs2020-go/simconnect](simconnect/) package currently only implements enough of the simconnect api for [examples](examples/) and [vfrmap](vfrmap).

## download

program zips are uploaded [here](https://github.com/lian/msfs2020-go/releases)

## tools

* [vfrmap](vfrmap/) web server that shows your current MSFS2020 plane position in google maps inside the browser

## examples

* [examples/request_data](examples/request_data/) port of `MSFS-SDK/Samples/SimConnectSamples/RequestData/RequestData.cpp`

46 changes: 24 additions & 22 deletions examples/request_data/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@ import (

type Report struct {
simconnect.RecvSimobjectDataByType
Title [256]byte
Kohlsman float64
Altitude float64
Latitude float64
Longitude float64
Title [256]byte `name:"TITLE"`
Kohlsman float64 `name:"Kohlsman setting hg" unit:"inHg"`
Altitude float64 `name:"Plane Altitude" unit:"feet"`
Latitude float64 `name:"Plane Latitude" unit:"degrees"`
Longitude float64 `name:"Plane Longitude" unit:"degrees"`
}

func (r *Report) RequestData(s *simconnect.SimConnect) {
defineID := s.GetDefineID(r)
requestID := defineID
s.RequestDataOnSimObjectType(requestID, defineID, 0, simconnect.SIMOBJECT_TYPE_USER)
}

func main() {
Expand All @@ -26,19 +32,15 @@ func main() {
}
fmt.Println("Connected to Flight Simulator!")

defineID := simconnect.DWORD(0)
s.AddToDataDefinition(defineID, "Title", "", simconnect.DATATYPE_STRING256)
s.AddToDataDefinition(defineID, "Kohlsman setting hg", "inHg", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "Plane Altitude", "feet", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "Plane Latitude", "degrees", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "Plane Longitude", "degrees", simconnect.DATATYPE_FLOAT64)
report := &Report{}
s.RegisterDataDefinition(report)
report.RequestData(s)

fmt.Println("SubscribeToSystemEvent")
eventSimStartID := simconnect.DWORD(0)
s.SubscribeToSystemEvent(eventSimStartID, "SimStart")

requestID := simconnect.DWORD(0)
s.RequestDataOnSimObjectType(requestID, defineID, 0, simconnect.SIMOBJECT_TYPE_USER)
/*
fmt.Println("SubscribeToSystemEvent")
eventSimStartID := simconnect.DWORD(0)
s.SubscribeToSystemEvent(eventSimStartID, "SimStart")
*/

for {
ppData, r1, err := s.GetNextDispatch()
Expand Down Expand Up @@ -70,8 +72,8 @@ func main() {
//spew.Dump(recvEvent)

switch recvEvent.EventID {
case eventSimStartID:
fmt.Println("SimStart Event")
//case eventSimStartID:
// fmt.Println("SimStart Event")
default:
fmt.Println("unknown SIMCONNECT_RECV_ID_EVENT", recvEvent.EventID)
}
Expand All @@ -81,10 +83,10 @@ func main() {
fmt.Println("SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE")

switch recvData.RequestID {
case requestID:
report := *(*Report)(ppData)
case s.DefineMap["Report"]:
report := (*Report)(ppData)
fmt.Printf("REPORT: %s: GPS: %.6f,%.6f Altitude: %.0f\n", report.Title, report.Latitude, report.Longitude, report.Altitude)
s.RequestDataOnSimObjectType(requestID, defineID, 0, simconnect.SIMOBJECT_TYPE_USER)
report.RequestData(s)
}

default:
Expand Down
32 changes: 32 additions & 0 deletions simconnect/defs.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package simconnect

import "fmt"

// MSFS-SDK/SimConnect\ SDK/include/SimConnect.h

const E_FAIL uint32 = 0x80004005
Expand Down Expand Up @@ -30,6 +32,36 @@ const (
DATATYPE_MAX // enum limit
)

func derefDataType(fieldType string) (DWORD, error) {
var dataType DWORD
switch fieldType {
case "int32":
dataType = DATATYPE_INT32
case "int64":
dataType = DATATYPE_INT64
case "float32":
dataType = DATATYPE_FLOAT32
case "float64":
dataType = DATATYPE_FLOAT64
case "[8]byte":
dataType = DATATYPE_STRING8
case "[32]byte":
dataType = DATATYPE_STRING32
case "[64]byte":
dataType = DATATYPE_STRING64
case "[128]byte":
dataType = DATATYPE_STRING128
case "[256]byte":
dataType = DATATYPE_STRING256
case "[260]byte":
dataType = DATATYPE_STRING260
default:
return 0, fmt.Errorf("DATATYPE not implemented: %s", fieldType)
}

return dataType, nil
}

const (
RECV_ID_NULL DWORD = iota
RECV_ID_EXCEPTION
Expand Down
51 changes: 49 additions & 2 deletions simconnect/simconnect.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"syscall"
"unsafe"
)
Expand All @@ -22,11 +23,14 @@ var proc_SimConnect_GetNextDispatch *syscall.LazyProc
var proc_SimConnect_RequestDataOnSimObjectType *syscall.LazyProc

type SimConnect struct {
handle unsafe.Pointer
handle unsafe.Pointer
DefineMap map[string]DWORD
}

func New(name string) (*SimConnect, error) {
s := &SimConnect{}
s := &SimConnect{
DefineMap: map[string]DWORD{"_last": 0},
}

if proc_SimConnect_Open == nil {
exePath, err := os.Executable()
Expand Down Expand Up @@ -81,6 +85,49 @@ func New(name string) (*SimConnect, error) {
return s, nil
}

func (s *SimConnect) GetDefineID(a interface{}) DWORD {
structName := reflect.TypeOf(a).Elem().Name()

id, ok := s.DefineMap[structName]
if !ok {
id = s.DefineMap["_last"]
s.DefineMap[structName] = id
s.DefineMap["_last"] = id + 1
}

return id
}

func (s *SimConnect) RegisterDataDefinition(a interface{}) error {
defineID := s.GetDefineID(a)

v := reflect.ValueOf(a).Elem()
for j := 1; j < v.NumField(); j++ {
fieldName := v.Type().Field(j).Name
nameTag, _ := v.Type().Field(j).Tag.Lookup("name")
unitTag, _ := v.Type().Field(j).Tag.Lookup("unit")

fieldType := v.Field(j).Kind().String()
if fieldType == "array" {
fieldType = fmt.Sprintf("[%d]byte", v.Field(j).Type().Len())
}

if nameTag == "" {
return fmt.Errorf("%s name tag not found", fieldName)
}

dataType, err := derefDataType(fieldType)
if err != nil {
return err
}

s.AddToDataDefinition(defineID, nameTag, unitTag, dataType)
//fmt.Printf("fieldName: %s fieldType: %s nameTag: %s unitTag: %s\n", fieldName, fieldType, nameTag, unitTag)
}

return nil
}

func (s *SimConnect) Close() error {
// SimConnect_Open(
// HANDLE * phSimConnect,
Expand Down
30 changes: 30 additions & 0 deletions vfrmap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# msfs2020-go/vfrmap

web server that shows your current MSFS2020 plane position in google maps inside the browser

## install

* download latest release zip [here](https://github.com/lian/msfs2020-go/releases)
* unzip `vfrmap-win64.zip`

## run
* run `vfrmap.exe`
* browse to http://localhost:9000

## arguments

* `-v` show program version
* `-api-key` use your own gmap api-key
* `-verbose` verbose output

## change visualisation

if you want to change how the webpage looks then copy and change [index.html](html/index.html) to the same folder as `vfrmap.exe` and relaunch the program.

## compile

`GOOS=windows GOARCH=amd64 go build github.com/lian/msfs2020-go/vfrmap` or see [build-vfrmap.sh](https://github.com/lian/msfs2020-go/blob/master/build-vfrmap.sh)

## screenshots

![screenshot](https://i.imgur.com/YllMEvG.png)
58 changes: 27 additions & 31 deletions vfrmap/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,22 @@ import (

type Report struct {
simconnect.RecvSimobjectDataByType
Title [256]byte
Altitude float64
Latitude float64
Longitude float64
Heading float64
Airspeed float64
VerticalSpeed float64
Flaps float64
Trim float64
RudderTrim float64
Title [256]byte `name:"TITLE"`
Altitude float64 `name:"INDICATED ALTITUDE" unit:"feet"` // PLANE ALTITUDE or PLANE ALT ABOVE GROUND
Latitude float64 `name:"PLANE LATITUDE" unit:"degrees"`
Longitude float64 `name:"PLANE LONGITUDE" unit:"degrees"`
Heading float64 `name:"PLANE HEADING DEGREES TRUE" unit:"degrees"`
Airspeed float64 `name:"AIRSPEED INDICATED" unit:"knot"`
VerticalSpeed float64 `name:"VERTICAL SPEED" unit:"ft/min"`
Flaps float64 `name:"TRAILING EDGE FLAPS LEFT ANGLE" unit:"degrees"`
Trim float64 `name:"ELEVATOR TRIM PCT" unit:"percent"`
RudderTrim float64 `name:"RUDDER TRIM PCT" unit:"percent"`
}

func (r *Report) RequestData(s *simconnect.SimConnect) {
defineID := s.GetDefineID(r)
requestID := defineID
s.RequestDataOnSimObjectType(requestID, defineID, 0, simconnect.SIMOBJECT_TYPE_USER)
}

var buildVersion string
Expand All @@ -46,7 +52,7 @@ var mapApiKey string
func main() {
flag.BoolVar(&showVersion, "v", false, "version")
flag.BoolVar(&verbose, "verbose", false, "verbose output")
flag.StringVar(&httpListen, "listen", "localhost:9000", "http listen")
flag.StringVar(&httpListen, "listen", "0.0.0.0:9000", "http listen")
flag.StringVar(&mapApiKey, "api-key", "", "gmap api-key")
flag.Parse()

Expand All @@ -71,29 +77,20 @@ func main() {
}
fmt.Println("Connected to Flight Simulator!")

defineID := simconnect.DWORD(0)
requestID := simconnect.DWORD(0)
s.AddToDataDefinition(defineID, "Title", "", simconnect.DATATYPE_STRING256)
s.AddToDataDefinition(defineID, "INDICATED ALTITUDE", "feet", simconnect.DATATYPE_FLOAT64)
//s.AddToDataDefinition(defineID, "PLANE ALT ABOVE GROUND", "feet", simconnect.DATATYPE_FLOAT64)
//s.AddToDataDefinition(defineID, "PLANE ALTITUDE", "feet", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "PLANE LATITUDE", "degrees", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "PLANE LONGITUDE", "degrees", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "PLANE HEADING DEGREES TRUE", "degrees", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "AIRSPEED INDICATED", "knot", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "VERTICAL SPEED", "ft/min", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "TRAILING EDGE FLAPS LEFT ANGLE", "degrees", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "ELEVATOR TRIM PCT", "percent", simconnect.DATATYPE_FLOAT64)
s.AddToDataDefinition(defineID, "RUDDER TRIM PCT", "percent", simconnect.DATATYPE_FLOAT64)
report := &Report{}
err = s.RegisterDataDefinition(report)
if err != nil {
panic(err)
}

report.RequestData(s)

/*
fmt.Println("SubscribeToSystemEvent")
eventSimStartID := simconnect.DWORD(0)
s.SubscribeToSystemEvent(eventSimStartID, "SimStart")
*/

s.RequestDataOnSimObjectType(requestID, defineID, 0, simconnect.SIMOBJECT_TYPE_USER)

go func() {
for {
ppData, r1, err := s.GetNextDispatch()
Expand Down Expand Up @@ -136,9 +133,8 @@ func main() {
//fmt.Println("SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE")

switch recvData.RequestID {
case requestID:
report := *(*Report)(ppData)
//fmt.Printf("REPORT: %s: GPS: %.6f,%.6f Altitude: %.0f Heading: %.1f\n", report.Title, report.Latitude, report.Longitude, report.Altitude, report.Heading)
case s.DefineMap["Report"]:
report = (*Report)(ppData)

if verbose {
fmt.Printf("REPORT: %#v\n", report)
Expand All @@ -156,7 +152,7 @@ func main() {
"rudder_trim": fmt.Sprintf("%.1f", report.RudderTrim),
})

s.RequestDataOnSimObjectType(requestID, defineID, 0, simconnect.SIMOBJECT_TYPE_USER)
report.RequestData(s)
}

default:
Expand Down

0 comments on commit 84f5d13

Please sign in to comment.