compile success with go 1.16.4, large expansion uwu
This commit is contained in:
362
arma.go
Normal file
362
arma.go
Normal file
@@ -0,0 +1,362 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "extensionCallback.h"
|
||||
*/
|
||||
import "C" // This is required to import the C code
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
influxdb2 "github.com/influxdata/influxdb-client-go/v2"
|
||||
)
|
||||
|
||||
var EXTENSION_VERSION string = "0.0.1"
|
||||
var extensionCallbackFnc C.extensionCallback
|
||||
var influxConnectionSettings influxSettings
|
||||
var a3Settings arma3Settings
|
||||
|
||||
// InfluxDB variables
|
||||
var DB_CLIENT influxdb2.Client
|
||||
|
||||
// file paths
|
||||
var ADDON_FOLDER string = "./@RangerMetrics"
|
||||
var LOG_FILE string = ADDON_FOLDER + "/rangermetrics.log"
|
||||
var SETTINGS_FILE string = ADDON_FOLDER + "/settings.json"
|
||||
|
||||
// configure log output
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
// log to file
|
||||
f, err := os.OpenFile(LOG_FILE, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Fatalf("error opening file: %v", err)
|
||||
}
|
||||
// log to console as well
|
||||
log.SetOutput(io.MultiWriter(f, os.Stdout))
|
||||
}
|
||||
|
||||
// func RVExtensionContext(output *C.char, argc *C.int) {
|
||||
|
||||
// }
|
||||
|
||||
type influxSettings struct {
|
||||
Host string `json:"host"`
|
||||
Token string `json:"token"`
|
||||
Org string `json:"org"`
|
||||
DefaultBucket string `json:"defaultBucket"`
|
||||
}
|
||||
|
||||
type arma3Settings struct {
|
||||
RefreshRateMs int `json:"refreshRateMs"`
|
||||
}
|
||||
|
||||
type settingsJson struct {
|
||||
Influx influxSettings `json:"influxdb"`
|
||||
Arma3 arma3Settings `json:"arma3"`
|
||||
}
|
||||
|
||||
func connectToInflux() string {
|
||||
if influxConnectionSettings.Host == "" {
|
||||
logLine("connectToInflux", `["influxConnectionSettings.Host is empty", "ERROR"]`)
|
||||
return "ERROR"
|
||||
}
|
||||
|
||||
DB_CLIENT = influxdb2.NewClientWithOptions(influxConnectionSettings.Host, influxConnectionSettings.Token, influxdb2.DefaultOptions().SetBatchSize(500).SetFlushInterval(2000))
|
||||
|
||||
logLine("connectToInflux", `["DB_CLIENT created", "INFO"]`)
|
||||
return "CONNECTED"
|
||||
}
|
||||
|
||||
func deinitialize() {
|
||||
logLine("deinitialize", `["deinitialize called", "INFO"]`)
|
||||
DB_CLIENT.Close()
|
||||
}
|
||||
|
||||
func getDir() string {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
func loadSettings() (dir string, result string, host string) {
|
||||
logLine("loadSettings", fmt.Sprintf(`["ADDON_FOLDER: %s", "INFO"]`, ADDON_FOLDER))
|
||||
logLine("loadSettings", fmt.Sprintf(`["LOG_FILE: %s", "INFO"]`, LOG_FILE))
|
||||
logLine("loadSettings", fmt.Sprintf(`["SETTINGS_FILE: %s", "INFO"]`, SETTINGS_FILE))
|
||||
|
||||
// print the current working directory
|
||||
var file *os.File
|
||||
var err error
|
||||
// read settings from file
|
||||
// settings.json should be in the same directory as the .dll
|
||||
// see if the file exists
|
||||
if _, err = os.Stat(SETTINGS_FILE); os.IsNotExist(err) {
|
||||
// file does not exist
|
||||
log.Println("settings.json does not exist")
|
||||
// create the file
|
||||
file, err = os.Create(SETTINGS_FILE)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
// write the default settings to the file
|
||||
ifSet := influxSettings{
|
||||
Host: "http://localhost:8086",
|
||||
Token: "my-token",
|
||||
Org: "my-org",
|
||||
DefaultBucket: "my-bucket",
|
||||
}
|
||||
a3Set := arma3Settings{
|
||||
RefreshRateMs: 1000,
|
||||
}
|
||||
defaultSettings := map[string]interface{}{
|
||||
"influxdb": ifSet,
|
||||
"arma3": a3Set,
|
||||
}
|
||||
encoder := json.NewEncoder(file)
|
||||
err = encoder.Encode(defaultSettings)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
result = `["settings.json created - please modify!", "WARN"]`
|
||||
host = ifSet.Host
|
||||
return dir, result, host
|
||||
} else {
|
||||
// file exists
|
||||
log.Println("settings.json exists")
|
||||
// read the file
|
||||
file, err = os.Open(SETTINGS_FILE)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
decoder := json.NewDecoder(file)
|
||||
var settings settingsJson
|
||||
err = decoder.Decode(&settings)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// set the settings
|
||||
influxConnectionSettings = settings.Influx
|
||||
a3Settings = settings.Arma3
|
||||
|
||||
// set the result
|
||||
result = `["settings.json loaded", "INFO"]`
|
||||
host = influxConnectionSettings.Host
|
||||
}
|
||||
|
||||
return dir, result, host
|
||||
}
|
||||
|
||||
func runExtensionCallback(name *C.char, function *C.char, data *C.char) C.int {
|
||||
return C.runExtensionCallback(extensionCallbackFnc, name, function, data)
|
||||
}
|
||||
|
||||
//export goRVExtensionVersion
|
||||
func goRVExtensionVersion(output *C.char, outputsize C.size_t) {
|
||||
result := C.CString(EXTENSION_VERSION)
|
||||
defer C.free(unsafe.Pointer(result))
|
||||
var size = C.strlen(result) + 1
|
||||
if size > outputsize {
|
||||
size = outputsize
|
||||
}
|
||||
C.memmove(unsafe.Pointer(output), unsafe.Pointer(result), size)
|
||||
}
|
||||
|
||||
//export goRVExtensionArgs
|
||||
func goRVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv **C.char, argc C.int) {
|
||||
var offset = unsafe.Sizeof(uintptr(0))
|
||||
var out []string
|
||||
for index := C.int(0); index < argc; index++ {
|
||||
out = append(out, C.GoString(*argv))
|
||||
argv = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + offset))
|
||||
}
|
||||
temp := fmt.Sprintf("Function: %s nb params: %d params: %s!", C.GoString(input), argc, out)
|
||||
|
||||
// Return a result to Arma
|
||||
result := C.CString(temp)
|
||||
defer C.free(unsafe.Pointer(result))
|
||||
var size = C.strlen(result) + 1
|
||||
if size > outputsize {
|
||||
size = outputsize
|
||||
}
|
||||
|
||||
if C.GoString(input) == "sendToInflux" {
|
||||
// start a goroutine to send the data to influx
|
||||
// param string is argv[0] which is the data to send to influx
|
||||
// go sendToInflux(out)
|
||||
go sendToInflux(&out)
|
||||
}
|
||||
|
||||
C.memmove(unsafe.Pointer(output), unsafe.Pointer(result), size)
|
||||
}
|
||||
|
||||
func callBackExample() {
|
||||
name := C.CString("arma")
|
||||
defer C.free(unsafe.Pointer(name))
|
||||
function := C.CString("funcToExecute")
|
||||
defer C.free(unsafe.Pointer(function))
|
||||
// Make a callback to Arma
|
||||
for i := 0; i < 3; i++ {
|
||||
time.Sleep(2 * time.Second)
|
||||
param := C.CString(fmt.Sprintf("Loop: %d", i))
|
||||
defer C.free(unsafe.Pointer(param))
|
||||
runExtensionCallback(name, function, param)
|
||||
}
|
||||
}
|
||||
|
||||
func getUnixTimeNano() int64 {
|
||||
// get the current unix timestamp in nanoseconds
|
||||
return time.Now().UnixNano()
|
||||
}
|
||||
|
||||
func trimQuotes(s string) string {
|
||||
// trim the start and end quotes from a string
|
||||
return strings.Trim(s, `"`)
|
||||
}
|
||||
|
||||
func fixEscapeQuotes(s string) string {
|
||||
// fix the escape quotes in a string
|
||||
return strings.Replace(s, `""`, `"`, -1)
|
||||
}
|
||||
|
||||
func sendToInflux(a3DataRaw *[]string) string {
|
||||
|
||||
// convert to string array
|
||||
a3Data := *a3DataRaw
|
||||
|
||||
var err error
|
||||
|
||||
logLine("sendToInflux", fmt.Sprintf(`["Received %d params", "DEBUG"]`, len(a3Data)))
|
||||
|
||||
MIN_PARAMS_COUNT := 1
|
||||
|
||||
var logData string
|
||||
functionName := "sendToInflux"
|
||||
|
||||
if len(a3Data) < MIN_PARAMS_COUNT {
|
||||
logData = fmt.Sprintf(`["Not all parameters present (got %d, expected at least %d)", "ERROR"]`, len(a3Data), MIN_PARAMS_COUNT)
|
||||
logLine(functionName, logData)
|
||||
return logData
|
||||
}
|
||||
|
||||
// use custom bucket or default
|
||||
var customBucket string = trimQuotes(string(a3Data[0]))
|
||||
bucket := influxConnectionSettings.DefaultBucket
|
||||
if customBucket != "" {
|
||||
bucket = customBucket
|
||||
}
|
||||
|
||||
WRITE_API := DB_CLIENT.WriteAPI(influxConnectionSettings.Org, bucket)
|
||||
|
||||
if WRITE_API == nil {
|
||||
logData = `["Error creating write API", "ERROR"]`
|
||||
logLine(functionName, logData)
|
||||
return logData
|
||||
}
|
||||
|
||||
// now we have our write client, we'll go through the rest of the receive array items in line protocol format and write them to influx
|
||||
for i := 1; i < len(a3Data); i++ {
|
||||
var p string = fixEscapeQuotes(trimQuotes(string(a3Data[i])))
|
||||
|
||||
WRITE_API.WriteRecord(p)
|
||||
|
||||
if err != nil {
|
||||
logData = fmt.Sprintf(`["Error parsing line protocol: %s", "ERROR"]`, err.Error())
|
||||
logLine(functionName, logData)
|
||||
return logData
|
||||
}
|
||||
}
|
||||
|
||||
// schedule cleanup
|
||||
WRITE_API.Flush()
|
||||
logData = fmt.Sprintf(`["Wrote %d lines to influx", "INFO"]`, len(a3Data)-1)
|
||||
logLine(functionName, logData)
|
||||
|
||||
return "Success"
|
||||
}
|
||||
|
||||
func logLine(functionName string, data string) {
|
||||
statusName := C.CString("RangerMetrics")
|
||||
defer C.free(unsafe.Pointer(statusName))
|
||||
statusFunction := C.CString(functionName)
|
||||
defer C.free(unsafe.Pointer(statusFunction))
|
||||
statusParam := C.CString(data)
|
||||
defer C.free(unsafe.Pointer(statusParam))
|
||||
runExtensionCallback(statusName, statusFunction, statusParam)
|
||||
|
||||
log.Println(data)
|
||||
}
|
||||
|
||||
//export goRVExtension
|
||||
func goRVExtension(output *C.char, outputsize C.size_t, input *C.char) {
|
||||
|
||||
var temp string
|
||||
|
||||
switch C.GoString(input) {
|
||||
case "version":
|
||||
logLine("goRVExtension", fmt.Sprintf(`["Input: %s", "INFO"]`, C.GoString(input)))
|
||||
temp = EXTENSION_VERSION
|
||||
case "getDir":
|
||||
logLine("goRVExtension", fmt.Sprintf(`["Input: %s", "INFO"]`, C.GoString(input)))
|
||||
temp = getDir()
|
||||
case "loadSettings":
|
||||
logLine("goRVExtension", fmt.Sprintf(`["Input: %s", "INFO"]`, C.GoString(input)))
|
||||
cwd, result, influxHost := loadSettings()
|
||||
log.Println("CWD:", cwd)
|
||||
log.Println("RESULT:", result)
|
||||
log.Println("INFLUX HOST:", influxHost)
|
||||
if result != "" {
|
||||
logLine("goRVExtension", result)
|
||||
temp = fmt.Sprintf(
|
||||
`["%s", "%s", "%s", "%s", "%d"]`,
|
||||
EXTENSION_VERSION,
|
||||
influxConnectionSettings.Host,
|
||||
influxConnectionSettings.Org,
|
||||
influxConnectionSettings.DefaultBucket,
|
||||
a3Settings.RefreshRateMs,
|
||||
)
|
||||
}
|
||||
case "connectToInflux":
|
||||
logLine("goRVExtension", fmt.Sprintf(`["Input: %s", "INFO"]`, C.GoString(input)))
|
||||
result := connectToInflux()
|
||||
temp = fmt.Sprintf(`["%s", "INFO"]`, result)
|
||||
case "getUnixTimeNano":
|
||||
temp = fmt.Sprintf(`["%d", "INFO"]`, getUnixTimeNano())
|
||||
case "deinitialize":
|
||||
logLine("goRVExtension", fmt.Sprintf(`["Input: %s", "INFO"]`, C.GoString(input)))
|
||||
deinitialize()
|
||||
temp = `["Deinitializing", "INFO"]`
|
||||
default:
|
||||
temp = fmt.Sprintf(`["Unknown command: %s", "ERR"]`, C.GoString(input))
|
||||
}
|
||||
|
||||
result := C.CString(temp)
|
||||
defer C.free(unsafe.Pointer(result))
|
||||
var size = C.strlen(result) + 1
|
||||
if size > outputsize {
|
||||
size = outputsize
|
||||
}
|
||||
|
||||
C.memmove(unsafe.Pointer(output), unsafe.Pointer(result), size)
|
||||
// return
|
||||
}
|
||||
|
||||
//export goRVExtensionRegisterCallback
|
||||
func goRVExtensionRegisterCallback(fnc C.extensionCallback) {
|
||||
extensionCallbackFnc = fnc
|
||||
}
|
||||
|
||||
func main() {}
|
||||
Reference in New Issue
Block a user