steve2112 Posted January 3, 2016 Posted January 3, 2016 (edited) i've finally got something working after a day and a bit of frustration, so i thought i would share for other people interested in getting real time telemetry data form DCS. many many many thanks to jboecker at DCS-BIOS for all his patience and help with my stupid question. so basically, you can talk to DCS as a programmer using a file called Exports.lua in your DCS/Scripts folder here C:\Users\me\Saved Games\DCS\Scripts or here C:\Users\me\Saved Games\DCS.openbeta\Scripts if you are on 1.5 so here's my Exports.lua local log_file = nil package.path = package.path..";.\\LuaSocket\\?.lua" package.cpath = package.cpath..";.\\LuaSocket\\?.dll" socket = require("socket") host = host or "localhost" port = port or 27015 function LuaExportStart() log_file = io.open("C:/Users/me/Saved Games/DCS/Logs/Export.log", "w") end function LuaExportBeforeNextFrame() end function LuaExportAfterNextFrame() end function LuaExportStop() if log_file then log_file:write("Closing log file.") log_file:close() log_file = nil end if connectSoc then socket.try(connectSoc:send("quit")) -- to close the listener socket connectSoc:close() end end function LuaExportActivityNextEvent(t) if connectSoc==nil then log_file:write("try to open socket\n") connectSoc = socket.try(socket.connect(host, port)) -- connect to the listener socket connectSoc:setoption("tcp-nodelay",true) -- set immediate transmission mode if connectSoc then log_file:write("socket opened ok\n") socket.try(connectSoc:send(string.format("Hello from DCS\n"))) else log_file:write("socket open failed\n") return t+0.5 end end local altBar = LoGetAltitudeAboveSeaLevel() local altRad = LoGetAltitudeAboveGroundLevel() local pitch, bank, yaw = LoGetADIPitchBankYaw() local tas = LoGetTrueAirSpeed() socket.try(connectSoc:send(string.format( "altBar=%.2f, pitch=%.2f, bank=%.2f tas=%.2f\n", altBar, 57.3*pitch, 57.3*bank, tas))) local o = LoGetWorldObjects() for k,v in pairs(o) do -- if v.UnitName=="steve" then socket.try(connectSoc:send(string.format( "name=%s UnitName=%s LatLong=(%f,%f)\n", v.Name, v.UnitName, v.LatLongAlt.Lat, v.LatLongAlt.Long))) -- end end return t+0.5 end so all you need to do on the DCS side is put that in the Scripts folder and run DCS then the bit that took me a while to work out. That export script is basically a client application, it attempts to connect to a TCP server on port 27015. if you don't know what this sentence means, you need to do a lot of reading on networking and TCPIP. so you need to write a server to listen to port 27015. and here it is (on windows) // astest.cpp : Defines the entry point for the console application. // #include "stdafx.h" #undef UNICODE #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <stdlib.h> #include <stdio.h> // Need to link with Ws2_32.lib #pragma comment (lib, "Ws2_32.lib") // #pragma comment (lib, "Mswsock.lib") #define DEFAULT_BUFLEN 512 #define DEFAULT_PORT "27015" int _tmain(int argc, _TCHAR* argv[]) { WSADATA wsaData; int iResult; SOCKET ListenSocket = INVALID_SOCKET; SOCKET ClientSocket = INVALID_SOCKET; struct addrinfo *result = NULL; struct addrinfo hints; int iSendResult; char recvbuf[DEFAULT_BUFLEN]; int recvbuflen = DEFAULT_BUFLEN; for(;;) { // Initialize Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed with error: %d\n", iResult); return 1; } ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; // Resolve the server address and port iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); if ( iResult != 0 ) { printf("getaddrinfo failed with error: %d\n", iResult); WSACleanup(); return 1; } printf("getaddrinfo OK\n"); // Create a SOCKET for connecting to server ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (ListenSocket == INVALID_SOCKET) { printf("socket failed with error: %ld\n", WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return 1; } printf("socket OK\n"); // Setup the TCP listening socket iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen); if (iResult == SOCKET_ERROR) { printf("bind failed with error: %d\n", WSAGetLastError()); freeaddrinfo(result); closesocket(ListenSocket); WSACleanup(); return 1; } printf("bind OK\n"); freeaddrinfo(result); iResult = listen(ListenSocket, SOMAXCONN); if (iResult == SOCKET_ERROR) { printf("listen failed with error: %d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } printf("listen OK\n"); // Accept a client socket ClientSocket = accept(ListenSocket, NULL, NULL); if (ClientSocket == INVALID_SOCKET) { printf("accept failed with error: %d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } printf("accept OK\n"); // No longer need server socket closesocket(ListenSocket); // Receive until the peer shuts down the connection do { iResult = recv(ClientSocket, recvbuf, recvbuflen, 0); if (iResult > 0) { //printf("Bytes received: %d\n", iResult); char* end = strchr(recvbuf, 10); if(end && end<&recvbuf[recvbuflen]) *end=0; printf("%s\n", recvbuf); } else if (iResult == 0) printf("Connection closing...\n"); else { printf("recv failed with error: %d\n", WSAGetLastError()); //closesocket(ClientSocket); //WSACleanup(); break; } } while (iResult > 0); // cleanup printf("closesocket OK\n"); iResult = shutdown(ClientSocket, SD_SEND); if (iResult == SOCKET_ERROR) { printf("shutdown failed with error: %d\n", WSAGetLastError()); closesocket(ClientSocket); WSACleanup(); return 1; } // cleanup closesocket(ClientSocket); WSACleanup(); } return 0; } so to use this code, get MSVC, start a new console app, and in your c++ source file, just paste all that, compile and run. now as you fly around in DCS, you should see data in the console window. the real trick here is the sync'ing of the network connections. i was having a really hard time to get the two things to start up reliably. so in the Export.lua file you will notice that i check to see if the connection is open on every call to LuaExportActivityNextEvent. if it isn't, i attempt to open it, and if ok, i start sending data. similarly on the windows server side, it needs to keep retrying to open a connection, so i took the example from here https://msdn.microsoft.com/en-us/library/windows/desktop/ms737593(v=vs.85).aspx and put the whole thing in a forever loop so it keeps retrying to connect. this way i was able to get the thing to run regardless of the startup sequence. (although sometime it doesn''t start and then i try again and it works, so still looking into that) this should work for any plane by the way, not just a10c there's probably a better way and i'd be happy to know about it, but at least i can say, this works. Edited January 3, 2016 by steve2112 1 My kit: i7-4790K@4GHz / 8GB - GTX 980ti + rift CV1 - X52 pro - Multi Keyboard Remapper - 2DOF motion sim (in development)
steve2112 Posted January 3, 2016 Author Posted January 3, 2016 (edited) ok, i've just found a better way by borrowing some ideas from DCS-BIOS, multicast! the nice thing is there's no connections to make and there no significant load on DCS i think. anyhow, its super simple, here's the exports.lua package.path = package.path..";.\\LuaSocket\\?.lua" package.cpath = package.cpath..";.\\LuaSocket\\?.dll" socket = require("socket") ipaddr = "239.255.50.10" port = 5010 -- Lua Export Functions function LuaExportStart() conn = socket.udp() conn:settimeout(0) end function LuaExportStop() socket.try(conn:close()) end function LuaExportBeforeNextFrame() end function LuaExportAfterNextFrame() socket.try(conn:sendto("hello from DCS\0", ipaddr , port)) local altBar = LoGetAltitudeAboveSeaLevel() local pitch, bank, yaw = LoGetADIPitchBankYaw() local tas = LoGetTrueAirSpeed() socket.try(conn:sendto(string.format( "altBar=%.2f, pitch=%.2f, bank=%.2f tas=%.2f\n", altBar, 57.3*pitch, 57.3*bank, tas), ipaddr , port)) local o = LoGetWorldObjects() for k,v in pairs(o) do -- if v.UnitName=="steve" then socket.try(conn:sendto(string.format( "name=%s UnitName=%s LatLong=(%f,%f)\n", v.Name, v.UnitName, v.LatLongAlt.Lat, v.LatLongAlt.Long), ipaddr , port)) -- end end end and the c++ program // DCS_airshow.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <winsock2.h> #include <Ws2tcpip.h> #include <stdio.h> // Link with ws2_32.lib #pragma comment(lib, "Ws2_32.lib") int _tmain(int argc, _TCHAR* argv[]) { char* multicast_ip = "239.255.50.10"; unsigned short multicast_port = 5010; SOCKADDR_IN multicast_addr; WSADATA wsaData; int hr; BOOL bOptVal = TRUE; ip_mreq mreq; int max_length = 16; WSAStartup(MAKEWORD(2,0), &wsaData); SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock == INVALID_SOCKET) { wprintf(L"socket failed with error %d\n", WSAGetLastError()); return 1; } // construct bind structure memset(&multicast_addr, 0, sizeof(multicast_addr)); multicast_addr.sin_family = AF_INET; multicast_addr.sin_addr.s_addr = htonl(INADDR_ANY); multicast_addr.sin_port = htons(multicast_port); hr = bind(sock, (struct sockaddr *) &multicast_addr, sizeof(multicast_addr)); if (hr != 0) { wprintf(L"bind failed with error %d\n", WSAGetLastError()); return 1; } /* Specify the multicast group */ mreq.imr_multiaddr.s_addr = inet_addr(multicast_ip); /* Accept multicast from any interface */ mreq.imr_interface.s_addr = htonl(INADDR_ANY); /* Join the multicast address */ hr = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char FAR *) &mreq, sizeof(mreq)); if (hr != 0) { wprintf(L"setsockopt failed with error %d\n", WSAGetLastError()); return 1; } int optval = 8; hr = setsockopt(sock,IPPROTO_IP,IP_MULTICAST_TTL, (char*)&optval,sizeof(int)); if (hr != 0) { wprintf(L"setsockopt failed with error %d\n", WSAGetLastError()); return 1; } hr = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&bOptVal, sizeof(bOptVal)); if (hr != 0) { wprintf(L"setsockopt failed with error %d\n", WSAGetLastError()); return 1; } int timeout = 1000; // 1 sec setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)); int cnt=0; while (true) { unsigned char buffer[4096] = { 0 }; int n = recvfrom(sock, (char*)buffer, 4096, 0, NULL, 0); if (n == SOCKET_ERROR) { //wprintf(L"%d recvfrom failed with error %d\n", cnt++, WSAGetLastError()); continue; } printf("%s\n", buffer); } WSACleanup(); return 0; } works great Edited January 3, 2016 by steve2112 My kit: i7-4790K@4GHz / 8GB - GTX 980ti + rift CV1 - X52 pro - Multi Keyboard Remapper - 2DOF motion sim (in development)
ineth Posted January 3, 2016 Posted January 3, 2016 Science Bitch Greek/German origin. Flying sims since 1984. Using computers since 1977. Favored FS's:F/A18 Interceptor, F19 Stealth Fighter, Gunnship, F16 Combat Pilot, Flight of the Intruder, A320, Falcon 4.0, MSFS 2004-X, DCS
Steggles Posted January 3, 2016 Posted January 3, 2016 Thank you sir :-) Answered some of my questions I never got round to asking. Thanks for taking the time to put this up. -16AGR- 16th Air Guards Regiment is always looking for pilots - http://www.16agr.com EWRS - Early Warning Radar Script Specs: Gigabyte Sniper Z5-S Intel i5-4670k 3.4GHz OC'd 3.9GHz w/ Thermaltake 120mm Water 3.0 Pro Liquid CPU Cooler 16GB RAM Gigabyte GTX 1080 TM Hotas Warthog: SN: 06976 Saitek Pro Flight Combat Rudder Pedals TrackIR5 with TrackClipPro & Oculus Rift 2x 28" 4k UHD Monitors (3840x2160 each) + 1280x1024
Hypo Posted January 3, 2016 Posted January 3, 2016 Science Bitch Can you explain what you mean by saying that? It seems very disrespectful saying that to someone who has helped out the DCS community. 1 ~S~ Hypo
ineth Posted January 3, 2016 Posted January 3, 2016 (edited) Can you explain what you mean by saying that? It seems very disrespectful saying that to someone who has helped out the DCS community. Oh, well it is not. Deffinetly not. Have you ever heard of a TV series called Breaking bad? That is a line from the series that gets used allot in various ermmmm variations. The original is [ame] [/ame] It is by no means disrespectful, on the contrary. I urge you to watch the series if you have not allready done so. and on a sidenote..... i am a bit rough on the edges at times but definitely not disrespectful. Thanks for allowing me to clear this up. Fly safe. Edited January 3, 2016 by ineth Greek/German origin. Flying sims since 1984. Using computers since 1977. Favored FS's:F/A18 Interceptor, F19 Stealth Fighter, Gunnship, F16 Combat Pilot, Flight of the Intruder, A320, Falcon 4.0, MSFS 2004-X, DCS
steve2112 Posted January 3, 2016 Author Posted January 3, 2016 i got the reference, BB is one of my all time fav shows. My kit: i7-4790K@4GHz / 8GB - GTX 980ti + rift CV1 - X52 pro - Multi Keyboard Remapper - 2DOF motion sim (in development)
Coug4r Posted January 9, 2016 Posted January 9, 2016 (edited) Great! Was looking for a heartbeat function for an external application, this was the first step in the right direction (it works). Is there a Lo* function that just returns the number of players? Or players units as alternative? Iterating through all units is a little overkill for just the # of players. Edited January 9, 2016 by Coug4r - If man were meant to fly he'd be filled with helium.
FSFIan Posted January 9, 2016 Posted January 9, 2016 Just a heads-up: to play nice with other applications that use Export.lua, you should store a reference to any existing callbacks before you redefine them and make sure to call them from your own callbacks. See the Lua code for Helios or DCS-BIOS for an example. For the same reason, you must avoid global variables as much as possible. In DCS-BIOS, I put everything into a single global table named BIOS or into file-local variables. This doesn't really matter as long as you only use it internally, but once you publish anything, you will have users that want to use it concurrently with other programs such as Helios or TARS. DCS-BIOS | How to export CMSP, RWR, etc. through MonitorSetup.lua
steve2112 Posted January 10, 2016 Author Posted January 10, 2016 Ian;2636740']Just a heads-up: to play nice with other applications that use Export.lua, you should store a reference to any existing callbacks before you redefine them and make sure to call them from your own callbacks. See the Lua code for Helios or DCS-BIOS for an example. For the same reason, you must avoid global variables as much as possible. In DCS-BIOS, I put everything into a single global table named BIOS or into file-local variables. This doesn't really matter as long as you only use it internally, but once you publish anything, you will have users that want to use it concurrently with other programs such as Helios or TARS. thanks for pointing that out, i did notice that code in DCS-BIOS and like you said, my code is just an example, and i wanted simplicity most of all to demonstrate the point, but the right way to do it is what you suggest. thx My kit: i7-4790K@4GHz / 8GB - GTX 980ti + rift CV1 - X52 pro - Multi Keyboard Remapper - 2DOF motion sim (in development)
Scooternutz Posted January 10, 2016 Posted January 10, 2016 Can you explain what you mean by saying that? It seems very disrespectful saying that to someone who has helped out the DCS community. OH JEEZ! [sIGPIC]https://drive.google.com/file/d/16rUBmmJR7A3YGZVGPGskxG1XtvulGojJ/view?usp=sharing[/sIGPIC]
cercata Posted December 17, 2021 Posted December 17, 2021 (edited) Is this code still valid for the actual version of DCS ? Where can I find info about what values can be obtained beside these ones ? I would be intereseted in g-forces, impacts, ... if for a haptic vest local altBar = LoGetAltitudeAboveSeaLevel() local pitch, bank, yaw = LoGetADIPitchBankYaw() local tas = LoGetTrueAirSpeed() Edited December 17, 2021 by cercata
feefifofum Posted December 17, 2021 Posted December 17, 2021 The Hoggit scripting wiki should have most of your answers - https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation 1 THE GEORGIAN WAR - OFFICIAL F-15C DLC
cercata Posted April 7, 2022 Posted April 7, 2022 (edited) This wiki es wonderfull: https://wiki.hoggitworld.com/view/DCS_export I have also found a nice export script for UDP, from the FLYPT mover: https://www.xsimulator.net/community/attachments/export-lua-txt.64152/ Edited April 7, 2022 by cercata
Recommended Posts