ISI Sample plugin sending data into a memory map file.

Discussion in 'Plugins' started by infiltrado, Oct 22, 2016.

  1. infiltrado

    infiltrado Registered

    Joined:
    Jul 7, 2016
    Messages:
    11
    Likes Received:
    1
    Hello guys, I am developing a custom plugin based to sample, but I have troubles to send data to a memory map file, using sample to a text file is OK, no problem, but I think that way is not the best if I search performance, I think it make loose frame rate.

    So I ask for help to developing the plugin adapting it to write data in a memory map file, do you have any sample?

    Thanks in advance and sorry if I make write mistakes, English is not my primary language.
     
  2. Lazza

    Lazza Registered

    Joined:
    Oct 5, 2010
    Messages:
    7,407
    Likes Received:
    3,096
    Writing medium amounts of data to a file won't affect the frame rate as long as you don't flush the buffer every time. The example plugin closes the files after each write, which does affect performance.
     
  3. TheLeadWolf

    TheLeadWolf Registered

    Joined:
    Apr 13, 2016
    Messages:
    100
    Likes Received:
    6
  4. infiltrado

    infiltrado Registered

    Joined:
    Jul 7, 2016
    Messages:
    11
    Likes Received:
    1
    Ok, I was working in a project similar based, I can create the mem file with teh plugin and read with my C# app, but I have a new problem, the app don't read the mem file in order (I suposse), I will try to explain better:

    My shared structure in plugin is like this:

    Code:
    #ifndef DATOS_H
    #define DATOS_H
    
    struct DatosToConector
    {
            bool SessionRunning = false;
    	bool PlayerDriving = false;
    	long var1 = 0;	
    	long var2 = 0;
    	double var3 = 0;
    	double var4 = 0;
    
    }
    This is the way I assigned the values, I put a constant to check if the value I assigned is the value I get on C#, later I will change by the plugin data.

    Code:
    void ExampleInternalsPlugin::UpdateTelemetry(const TelemInfoV01 &info)
    {
    	telem->PlayerDriving = false;
    	telem->SessionRunning = false;
    	telem->var1 = 1;
    	telem->var2= 2;
    	telem->var3 = 3;
    	telem->var4= 4;
    }
    #endif

    And in the C# code:

    Code:
    [Serializable]
            [StructLayout(LayoutKind.Sequential, Pack = 4)]
            public struct DatosH
            {
                //[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
                public bool SessionRunning;
                public bool PlayerDriving;
                public long var1;
                public long var2;
                public double var3;
                public double var4;
    
               
            }
    And this is what I do to read memory file:

    Code:
    using (MemoryMappedFile memoryMappedFile = MemoryMappedFile.OpenExisting(memFile))
                    {
                        using (MemoryMappedViewStream viewStream = memoryMappedFile.CreateViewStream())
                        {
                            int count = Marshal.SizeOf(typeof(DatosH));
                            byte[] numArray = new byte[count];
                            _datosh = (DatosH)Marshal.PtrToStructure(GCHandle.Alloc((object)new BinaryReader((Stream)viewStream).ReadBytes(count), GCHandleType.Pinned).AddrOfPinnedObject(), typeof(DatosH));
    
                            return true;
                        }
                    }
    And this are my results:

    Code:
    SessionRunning =true
    PlayerDriving = false
    var1 = 2
    var2 = 4613937818241073152 	
    var3 = 4.0 	
    var4 = 0.0
    You can see that my vars don't get the expected results, but I don't know why. If I change the var type, for example:

    In plugin:

    Code:
    bool SessionRunning = false;
    	bool PlayerDriving = false;
    	int var1 = 0;	
    	int var2 = 0;
    	int var3 = 0;
    	int var4 = 0;
    I get this in C#:

    Code:
    SessionRunning 1 = true
    PlayerDriving 2 = false
    var1 = 2
    var2 = 3	
    var3 = 4	
    var4 = 0
    It seems the data type affect the results, but I think that the serialization must ignore the lengh of data and only set each var to his value.

    As you see, I comment the Marshall code in C# structure, if I uncomment Marshall I get this Visual Studio error when I read the mem file:

    Code:
    An unhandled exception of type 'System.ArgumentException' occurred in TestConsole.exe
    
    Additional information: Type 'TestConsole.Program+MyEnum' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
     
  5. TheLeadWolf

    TheLeadWolf Registered

    Joined:
    Apr 13, 2016
    Messages:
    100
    Likes Received:
    6
    I am a bit surprised you're using Serialization to read the file, but C# is not my primary language. Just a thought - I'd make C++ struct all int, and get that to work first. Also, you can see how rf1 shared memory is used here https://github.com/mrbelowski/CrewChiefV4 (note author was using pack(1) for his struct). Let us know how it goes, because I am planning to do those very same thing you're doing :) good luck
     
  6. infiltrado

    infiltrado Registered

    Joined:
    Jul 7, 2016
    Messages:
    11
    Likes Received:
    1
    After a few days studying the sample plugin and others projects in github, I think that I know how all works, I almost finished a beta app who receive data from dll, I hope finish by one or two weeks (Social live and work don't let me spend more time in rFactor)

    Of course, I will publish the results and code when I had a usable dll and app.
     
  7. TheLeadWolf

    TheLeadWolf Registered

    Joined:
    Apr 13, 2016
    Messages:
    100
    Likes Received:
    6
    What was the problem in your previous post and how did you solve it?
    Time is always a problem :D
     
  8. infiltrado

    infiltrado Registered

    Joined:
    Jul 7, 2016
    Messages:
    11
    Likes Received:
    1
    Ok, Today is not working day in Madrid and I can get some time for rFactor.

    First, the way I used to solve my problems was study a lot of github project and much stackoverflow and google.

    I usually works with C# but my area is web developing and it was my first time with Marshall Structures, and I didn't touch C++ afert my university days, some years ago...

    The dll plugin has not change so much, basically what I do is define my structure with basic variables:

    Code:
    #ifndef DATOS_H
    #define DATOS_H
    
    struct DatosToConector
    {
        bool SessionRunning = false;
        bool PlayerDriving = false;
        int mGear = 0; 
        int mSpeed = 0;
        int mRPM = 0;
        .
        .
        .
        .
        .
        //Put any variables as you need.
    }
    #endif
    
    void ExampleInternalsPlugin::UpdateTelemetry(const TelemInfoV01 &info)
    {
        telem->PlayerDriving = false;
        telem->SessionRunning = false;
        telem->mGear = (int)info.mGear;
        telem->mSpeed= mSpeed;
        telem->mRPM = (int)info.mEngineRPM;
     
        /*
        *mSpeed is a local variable not rfactor plugin data.
        */
    }

    And in the C# app these is my structure and the way I get data, the most important part is define the MarshalAs data type as you can see.

    Code:
    [Serializable]
    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    public struct DatosH
    {
        [MarshalAs(UnmanagedType.VariantBool)]
        public bool SessionRunning;
        [MarshalAs(UnmanagedType.VariantBool)]
        public bool PlayerDriving;
        [MarshalAs(UnmanagedType.I4)]
        public int mGear;
        public int mSpeed;
        public int mRPM;
        public int mMaxRPM;
        public int mPctRPM;
    
        public int mFrontLeftTemp;
        public int mFrontLeftWear;
        public int mFrontLeftBrakeTemp;
        public int mFrontLeftFlat;
    
        public int mFrontRightTemp;
        public int mFrontRightWear;
        public int mFrontRightBrakeTemp;
        public int mFrontRightFlat;
    
        public int mRearLeftTemp;
        public int mRearLeftWear;
        public int mRearLeftBrakeTemp;
        public int mRearLeftFlat;
    
        public int mRearRightTemp;
        public int mRearRightWear;
        public int mRearRightBrakeTemp;
        public int mRearRightFlat;
    
        public int mWater;
        public int mOil;
        public int mFuel;
        public int mHeating;
        public int mDRSLegal;
        public int mDRSStatus;
    
        public int mLapNumber;
        public int mCurrentSector;
    
        public double mCurrentET;
        public double mBestSector1;
        public double mBestSector2;
        public double mBestLapTime;
        public double mLastSector1;
        public double mLastSector2;
        public double mLastLapTime;
        public double mCurSector1;
        public double mCurSector2;
        public double mLapStartET;
    
        public double mEndET;
    
        public int mSpeedLimiter;
        public int mSpeedLimiterAvailable;
    }
    To get the data:

    Code:
    DatosH _datosh;
    
    string memFile = string.Empty;
    
    memFile = "Local\\SimTelemetryRfactor2";
    
    using (MemoryMappedFile memoryMappedFile = MemoryMappedFile.OpenExisting(memFile))
    {
        using (MemoryMappedViewStream viewStream = memoryMappedFile.CreateViewStream())
        {
            Type outputType = typeof(DatosH).IsEnum ? Enum.GetUnderlyingType(typeof(DatosH)) : typeof(DatosH);
    
            int count = Marshal.SizeOf(outputType);
            byte[] numArray = new byte[count];
            _datosh = (DatosH)Marshal.PtrToStructure(GCHandle.Alloc((object)new BinaryReader((Stream)viewStream).ReadBytes(count), GCHandleType.Pinned).AddrOfPinnedObject(), typeof(DatosH));
        }
    }
    And thats all, now you can acces to telemetry simply with

    Code:
    _datosh.mRearRightTemp
    _datosh.mRPM
    Or use your own structure for your proposes.

    Now I have a minor problems, I'm trying to get lap times, I do this:

    Code:
    string LapTime = "";
    tsCurrent = TimeSpan.FromSeconds(_datosh.mCurrentET - _datosh.mLapStartET);
    LapTime = tsCurrent.Minutes.ToString("00", (IFormatProvider)CultureInfo.InvariantCulture) + ":" + tsCurrent.Seconds.ToString("00", (IFormatProvider)CultureInfo.InvariantCulture) + ":" + tsCurrent.Milliseconds.ToString("000", (IFormatProvider)CultureInfo.InvariantCulture);
    But I observed that plugin send me _datosh.mCurrentET with only one decimal and I can't get the exact lap time, I always get some milisecons differences.

    My question is what are the bes way to get lap times (Delta time, best time, current time....)

    Thanks.
     
    TheLeadWolf likes this.
  9. Lazza

    Lazza Registered

    Joined:
    Oct 5, 2010
    Messages:
    7,407
    Likes Received:
    3,096
    Where are you getting mCurrentET from? The TelemInfoV01 structure provides mElapsedTime and mLapStartET, both doubles and updated at 100Hz because you're accessing them through UpdateTelemetry(). If you're instead pulling mCurrentET from UpdateScoring() it'll only be updated at 5Hz.

    What you're using should be fine for the current laptime, the last complete laptime you should either get the difference between mLapStartET values or use the timing data from UpdateScoring().

    *I'm not familiar with the C# code; you've set the marshalling size to I4 I think, do you need to change it for doubles? They're 8 bytes.
     
  10. The Iron Wolf

    The Iron Wolf Member

    Joined:
    Feb 20, 2016
    Messages:
    963
    Likes Received:
    906
    Thanks for sharing, this will save me time :) Are you planning to synchronize plugin file and reader, so that it doesn't read partially updated file?
    Also, are you doing any position math in your plugin? rF2 internals plugin has convertor factions from/to quaterions, and I wonder if you know when we need to use those?
     
    Last edited: Nov 10, 2016
  11. lordp

    lordp Registered

    Joined:
    Nov 21, 2016
    Messages:
    3
    Likes Received:
    3
    I am very interested in a shared memory map plugin for RF2. Willing to test any out too.
     
  12. The Iron Wolf

    The Iron Wolf Member

    Joined:
    Feb 20, 2016
    Messages:
    963
    Likes Received:
    906
    I am working on it, feel free to IM me, testing will help a lot.
     

Share This Page