313 lines
8.2 KiB
Plaintext
313 lines
8.2 KiB
Plaintext
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 (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"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
|
|
|
|
// configure log output
|
|
func init() {
|
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
|
// log to file
|
|
f, err := os.OpenFile("rangermetrics.log", 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"`
|
|
Bucket string `json:"bucket"`
|
|
}
|
|
|
|
func loadSettings() (dir string, result string, host string) {
|
|
// print the current working directory
|
|
|
|
var file *os.File
|
|
var err error
|
|
dir, err = os.Getwd()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
log.Println("getSettings", dir)
|
|
// 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.json"); os.IsNotExist(err) {
|
|
// file does not exist
|
|
log.Println("settings.json does not exist")
|
|
// create the file
|
|
file, err = os.Create("settings.json")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
// write the default settings to the file
|
|
defaultSettings := influxSettings{
|
|
Host: "http://localhost:8086",
|
|
Token: "my-token",
|
|
Org: "my-org",
|
|
Bucket: "my-bucket",
|
|
}
|
|
encoder := json.NewEncoder(file)
|
|
err = encoder.Encode(defaultSettings)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
result = "settings.json created - please modify!"
|
|
host = defaultSettings.Host
|
|
return dir, result, host
|
|
} else {
|
|
// file exists
|
|
log.Println("settings.json exists")
|
|
// read the file
|
|
file, err = os.Open("settings.json")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
decoder := json.NewDecoder(file)
|
|
err = decoder.Decode(&influxConnectionSettings)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
result = "settings.json read"
|
|
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)
|
|
}
|
|
|
|
//aexport goRVExtensionVersion
|
|
// func goRVExtensionVersion(output *C.char, outputsize C.size_t) {
|
|
// return
|
|
// 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) {
|
|
// return
|
|
// 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)
|
|
// }
|
|
|
|
// C.memmove(unsafe.Pointer(output), unsafe.Pointer(result), size)
|
|
// // return 1
|
|
// }
|
|
|
|
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 sanitize(s string) string {
|
|
t := strings.ReplaceAll(s, "\"", "")
|
|
return t
|
|
}
|
|
|
|
func sendToInflux(a3Data []string) {
|
|
EXPECTED_PARAMS := 8
|
|
|
|
var logData string
|
|
functionName := "sendToInflux"
|
|
|
|
if len(a3Data) < EXPECTED_PARAMS {
|
|
logData = fmt.Sprintf(`["Not all parameters present (got %d, expected %d)", "ERROR"]`, len(a3Data), EXPECTED_PARAMS)
|
|
logLine(functionName, logData, "ERROR")
|
|
return
|
|
}
|
|
|
|
// sanitize all elements
|
|
for i, v := range a3Data {
|
|
a3Data[i] = sanitize(v)
|
|
}
|
|
|
|
host := influxConnectionSettings.Host
|
|
token := influxConnectionSettings.Token
|
|
org := influxConnectionSettings.Org
|
|
bucket := influxConnectionSettings.Bucket
|
|
profile, locality := a3Data[0], a3Data[1]
|
|
missionName, worldName, serverName := a3Data[2], a3Data[3], a3Data[4]
|
|
metric := a3Data[5]
|
|
valueType := a3Data[6]
|
|
|
|
tags := map[string]string{
|
|
"profile": profile,
|
|
"locality": locality,
|
|
"worldName": worldName,
|
|
"serverName": serverName,
|
|
}
|
|
fields := map[string]interface{}{
|
|
"missionName": missionName,
|
|
// "count": value,
|
|
}
|
|
|
|
// parse the value
|
|
var err error
|
|
// allow for float or int values, but remove any auto backslashes
|
|
// check if includes certain strings
|
|
if strings.Contains(valueType, "float") {
|
|
fields["count"], err = strconv.ParseFloat(sanitize(a3Data[7]), 64)
|
|
} else if strings.Contains(valueType, "int") {
|
|
fields["count"], err = strconv.Atoi(sanitize(a3Data[7]))
|
|
} else {
|
|
logData = fmt.Sprintf("valueType must be 'float' or 'int': %s, %s, %s", metric, valueType, a3Data[7])
|
|
logLine(functionName, logData, "ERROR")
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
logData = fmt.Sprintf("Error parsing value: %s", err.Error())
|
|
logLine(functionName, logData, "ERROR")
|
|
return
|
|
}
|
|
|
|
// influxDB init
|
|
client := influxdb2.NewClientWithOptions(host, token, influxdb2.DefaultOptions().SetBatchSize(120))
|
|
writeAPI := client.WriteAPIBlocking(org, bucket)
|
|
|
|
p := influxdb2.NewPoint(metric, tags, fields, time.Now())
|
|
|
|
// write synchronously
|
|
err = writeAPI.WritePoint(context.Background(), p)
|
|
|
|
if err != nil {
|
|
logData = fmt.Sprintf("Error writing to InfluxDB: %s", err.Error())
|
|
logLine(functionName, logData, "ERROR")
|
|
}
|
|
|
|
defer client.Close()
|
|
|
|
logData = fmt.Sprintf("Sent to Influx: %s, %s", metric, a3Data[7])
|
|
logLine(functionName, logData, "INFO")
|
|
}
|
|
|
|
func logLine(functionName string, data string, resultType string) {
|
|
statusName := C.CString("RangerMetrics")
|
|
defer C.free(unsafe.Pointer(statusName))
|
|
statusFunction := C.CString(functionName)
|
|
defer C.free(unsafe.Pointer(statusFunction))
|
|
statusParam := C.CString(fmt.Sprintf(`["%s", "%s"]`, data, resultType))
|
|
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) {
|
|
temp := fmt.Sprintf("Hello %s!", C.GoString(input))
|
|
// 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
|
|
}
|
|
C.memmove(unsafe.Pointer(output), unsafe.Pointer(result), size)
|
|
|
|
// var temp string
|
|
|
|
// logLine("goRVExtension", fmt.Sprintf("Input: %s", C.GoString(input)), "INFO")
|
|
|
|
// switch C.GoString(input) {
|
|
// case "version":
|
|
// temp = fmt.Sprintf("%s", EXTENSION_VERSION)
|
|
// case "loadSettings":
|
|
// cwd, result, influxHost := loadSettings()
|
|
// log.Println("CWD:", cwd)
|
|
// log.Println("RESULT:", result)
|
|
// log.Println("INFLUX HOST:", influxHost)
|
|
// if result != "" {
|
|
// temp = fmt.Sprintf(`["CWD: %s", "RESULT: %s", "%s"]`, cwd, result, influxHost)
|
|
// }
|
|
// default:
|
|
// temp = fmt.Sprintf(`["ERR", "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
|
|
}
|
|
|
|
//aexport goRVExtensionRegisterCallback
|
|
// func goRVExtensionRegisterCallback(fnc C.extensionCallback) {
|
|
// return
|
|
// extensionCallbackFnc = fnc
|
|
// }
|
|
|
|
func main() {}
|