ATVO by Appgineering
Download
Read-only

Fast lap of each class?

24 posts 13,276 views Started 16 Feb 2019, 14:21
Showing 1–24 of 24 posts
Drew E.
Original poster

I have a widget to display when a new fast lap is set, is it possible to show the fast lap of each class or is this a limitation because iracing only counts the fastest lap of the session?

Nick Thissen Appgineering
Reply #2

We currently only track the overall session fastest lap, not per class. I am pretty sure also the iRacing data only tracks it globally, not per class. We do log every fastest lap of each driver (actually we log every lap, period) so in theory you could use a script to grab the fastest class lap manually.

I cooked this up quickly with no testing, but something similar should work I think?

using System;
using ATVO.ThemesSDK;
using ATVO.ThemesSDK.Data.Entity;
using ATVO.ThemeEditor.ThemeModels;
using ATVO.ThemeEditor.Scripting.DotNET;
using System.Collections.Generic;

namespace Scripts
{
    public class FastLapTracker : IScript
    {
        private Dictionary<byte, ILap> _fastlaps = new Dictionary<byte, ILap>();

        public object Execute(ThemeContentItem item, object value, string parameter, ISimulation sim)
        {
            // Loop over every entity session result and check for fastest lap in class
            foreach (var result in sim.Session.Current.Results)
            {
                // Get the class ID of this driver
                var classId = result.Entity.Car.Class.Id;

                // Get the currently recorded fastest lap for this driver's class
                // Note: the lap may not exist yet, in that case we add an entry for this class but keep the lap as null.
                ILap currentFastLap = null;
                if (_fastlaps.ContainsKey(classId))
                    currentFastLap = _fastlaps[classId];
                else
                    _fastlaps.Add(classId, null);

                // Get fastest lap of this driver
                var lap = result.FastestLap;
                
                // Check if the driver fastlap is faster than the currently stored fast lap
                // if there is no lap stored, this is a new lap and we consider it the fastest lap so far
                if (currentFastLap == null || currentFastLap.Time > lap.Time) 
                {
                    // "lap" is the newest fast lap of this class
                    // Replace it as the latest fast lap
                    _fastlaps[classId] = lap;

                    // Now trigger whatever you want to do, e.g. show a widget.
                    TriggerFastLap(item.Theme, result, lap);                        
                }
            }
        }

        private void TriggerFastLap(ITheme theme, IEntitySessionResult result, ILap lap)
        {
            // Just an example: find a widget named after the class ID
            var classId = result.Entity.Car.Class.Id;
            var widgetName = "Wigdet_Fastlap_Class" + classId;
            var widget = theme.Widgets.Find(widgetName);
            if (widget != null)
            {
                widget.Show();

                // Maybe start a timer that hides all fastlap widgets after few seconds
                var timer = theme.Timers.Find("FastLapHideTimer");
                timer.Start();
            }
            else 
            {
                // Log that this widget doesnt exist
                Console.WriteLine("Failed to find widget " + widgetName);
            }
        }
    }
}
Emmanuel S.
Reply #3

I have a look of this script exemple because I try to show a widget of theme with sript if driver had a name of team.

The system return that Show is not an extension method for Widget. So how we can show or hide a widget with a script ?

Nick Thissen Appgineering
Reply #4

I think a long time ago Show was replaced with AnimateShow to support the simple fade animations. You can call AnimateShow with a string that corresponds to the time in milliseconds for the fading time (or "0" for no fading).

widget.AnimateShow("0");
widget.AnimateShow("1000");

I will bring back 'Show' as a synonym for 'AnimateShow("0")', it was a mistake to remove it.

Emmanuel S.
Reply #5

Nice,, it is ok for action on theme widget. Now I search the good variable to check for team. I see the name team is allready exist (driver name) and it seems to have a variable if it is team or not (bool 0-1) but wich ?

Emmanuel S.
Reply #7
· edited

As I say in other area my VB don't want help me to find "variable".
I see in Logs files that Team have 2 informations (I think). First Name and second if session use Team as TeamID under.

CarIdx: 13
  UserName: Ian Barron
  ...
  TeamID: 0
  TeamName: Ian Barron
  CarNumber: "38"
....

So if it is right I need only to know if session use team (swap driver) to show my Widget.

EDIT: or just TeamID is 0 because it is not using in this session and other will be a real number TeamID: 32568 for exemple ? So check if TeamID = 0 ?

Nick Thissen Appgineering
Reply #8
· edited

We use the concept of "entity" which translates kind of to "entry in a race". This can be a driver, or a team with multiple drivers. To get an ID or name you don't need to know which it is, it depends on the kind of session. For example, the Name property of an entity will return the driver name in a single driver race, or the team name in a team race.

If the session is a team session, then any (https://github.com/appgineerin/ATVO-Themes-SDK/blob/master/ATVO.ThemesSDK/Data/Entity/IEntity.cs) will actually be of type (https://github.com/appgineerin/ATVO-Themes-SDK/blob/master/ATVO.ThemesSDK/Data/Entity/ITeam.cs) and you can get the team name from the Name and the team id from the Id properties.

If the session is a single driver race, then an entity is actuall (https://github.com/appgineerin/ATVO-Themes-SDK/blob/master/ATVO.ThemesSDK/Data/Entity/IDriver.cs) from which you can get more detailed information like Initials, LastName, etc.

In code:

// If you bind to 'entity_object':
var entity = (IEntity)value; 

// If you bind to 'entitysessionresult_object':
var result = (IEntitySessionResult)value; 
var entity = result.Entity;

var id = entity.Id;
var name = entity.Name;

// If it's a team session:
// - id will be Team ID, name will be Team name

// If it's a single driver session:
// - id will be driver customer ID, name will be driver name

// Check if it's a team
if (entity is ITeam)
{
 // Team object
 var driver = entity.CurrentDriver;
 var otherDrivers = entity.Drivers;
}
else 
{
 // Single driver 
 var driver = entity.CurrentDriver;
 
 // or this is probably the same:
 var driver = (IDriver)entity;
}
Emmanuel S.
Reply #9
· edited

Thanks a lot, I go step by step and certainly not by the good way :(

I only use the control if entity is Iteam to show my widget and it is OK.
But i need to assign the script to a label to get the binding to the script (in your exemple I first test to return driver if it is Iteam but tha value is not correct), so I decide to create a Widget only to get binding and I show an other Widget with TEAM NAME (no script for get team name).
But if it is possible to say directly into the script what I want to bind it will be better.

So now if I watch a Session with team, my widget with team name is automaticly visible, and I check with a no team session it stay OFF.

But I want to had this widget only when an other widget is visible (DRIVER INFO). I try to add a check for widget visibility, but again I have not the good "therms" to do it.

You can see my scripts that automaticaly show Team Name if session is team. But every time.

using System;
using ATVO.ThemesSDK;
using ATVO.ThemeEditor.ThemeModels;
using ATVO.ThemeEditor.Scripting.DotNET;
using ATVO.ThemesSDK.Data.Entity;
using System.Collections.Generic;
using ATVO.ThemesSDK.Data.Results;

namespace Scripts
{
 public class Sc_Pilot_Team : IScript
 
 {
 public object Execute(ThemeContentItem item, object value, string parameter, ISimulation sim)
 {
 if (value == null)
 {
     return null;
 }

IEntitySessionResult result = (IEntitySessionResult) value;

var entity = result.Entity;

var theme = item.Theme;
//find widget I want to show
var widget = theme.Widgets.Find("W_Pilote_Team");
//find widget to check VISIBLE for showing widget
var widget0 = theme.Widgets.Find("W_Pilote_Complet");

// Check if it's a team //I test if widget0.IsVisible == true but it is not OK
if (entity is ITeam)
{
widget.AnimateShow("0");
 
return null;
}
else 
return null;
}
}}
Emmanuel S.
Reply #10

Nick Thissen wrote:
// If you bind to 'entitysessionresult_object':
var result = (IEntitySessionResult)value;
var entity = result.Entity;

var id = entity.Id;
var name = entity.Name;

If I understand, if I use this part of code, "var name" will return TeamName if it is a Team Session and Driver Name in other case ?

Emmanuel S.
Reply #11
· edited

And if I return otherDrivers in a ticker when this is use :  **var otherDrivers = entity.Drivers; **does it show a driver list ?

Nick Thissen Appgineering
Reply #12

Sorry I'm a bit lost on your earlier (longer) question, I don't know what you're trying to ask.

Emmanuel S. wrote:
If I understand, if I use this part of code, "var name" will return TeamName if it is a Team Session and Driver Name in other case ?

Yes.

To get driver name in a team session, you have to use entity.CurrentDriver.Name instead.

Emmanuel S. wrote:
And if I return otherDrivers in a ticker when this is use :  **var otherDrivers = entity.Drivers; **does it show a driver list ?

entity.Drivers keeps a list of drivers that ATVO observed to get in the car. Unfortunately iRacing does not output a list of the drivers registered in a team, we can only know about a driver currently in the car. So if someone never got in the car yet, he will not make it into the Drivers list.

There is no dataset for team drivers (for this same reason) so you cannot use it in a ticker unfortunately. You could manually build a list of driver names and show that in a widget/label.

Emmanuel S.
Reply #13

Thanks

Nick Thissen wrote:
Sorry I'm a bit lost on your earlier (longer) question, I don't know what you're trying to ask.

Sorry, my english an C# are so bad ....

In summery, I just now want to know:

How use the visibilty of a Widget as condition to add to the team condition you explain me.

Nick Thissen Appgineering
Reply #14

A simple check for "IsVisible" should do it, no?

Emmanuel S.
Reply #15
· edited

I test but when I add IsVisible for the widget to check, the widget I want to show stay hide.

In the code below, if I remove the line:  if (widget0.IsVisible == true);
my widget is SHOW

With the control IsVisible I've got the error in Event Log : Scripts .... Object reference not set to an instance of an object

using System;
using ATVO.ThemesSDK;
using ATVO.ThemeEditor.ThemeModels;
using ATVO.ThemeEditor.Scripting.DotNET;
using ATVO.ThemesSDK.Data.Entity;
using System.Collections.Generic;
using ATVO.ThemesSDK.Data.Results;

namespace Scripts
{
 public class Sc_Pilot_Team : IScript
 
 {
 public object Execute(ThemeContentItem item, object value, string parameter, ISimulation sim)
 {
 if (value == null)
 {
     return null;
 }

IEntitySessionResult result = (IEntitySessionResult) value;

var entity = result.Entity;

var theme = item.Theme;
//find widget I want to show
var widget = theme.Widgets.Find("W_Pilote_Team");
//find widget to check VISIBLE for showing widget
var widget0 = theme.Widgets.Find("W_Pilote_Complet");

if (widget0.IsVisible == true);
 {
 if (entity is ITeam)
 {widget.AnimateShow("0"); 
 return null;}
 else 
 widget.AnimateHide("0");
 return null;
 }
}}}
Nick Thissen Appgineering
Reply #16

My guess would be there is no widget named "W_Pilote_Complet", so 'widget0' is null.

Emmanuel S.
Reply #17

My apologies, I don't see it is a space and not _ between Pilote Complet !! :'(

So it is ok with

if (entity is ITeam && widget0.IsVisible == true)

Just for information with widget.AnimateShow("0") it's ok, but if I put widget.AnimateShow("500") nothing happen. And with 1,2 or 3 the widget blinks.

Nick Thissen Appgineering
Reply #18

Weird, that should work just fine. Does the fading work in a normal Show widget action with a value of '500' set?

Emmanuel S.
Reply #20

Nick Thissen wrote:
We currently only track the overall session fastest lap, not per class. I am pretty sure also the iRacing data only tracks it globally, not per class. We do log every fastest lap of each driver (actually we log every lap, period) so in theory you could use a script to grab the fastest class lap manually.

I cooked this up quickly with no testing, but something similar should work I think?

using System;
using ATVO.ThemesSDK;
using ATVO.ThemesSDK.Data.Entity;
using ATVO.ThemeEditor.ThemeModels;
using ATVO.ThemeEditor.Scripting.DotNET;
using System.Collections.Generic;

namespace Scripts
{
    public class FastLapTracker : IScript
    {
        private Dictionary<byte, ILap> _fastlaps = new Dictionary<byte, ILap>();

        public object Execute(ThemeContentItem item, object value, string parameter, ISimulation sim)
        {
            // Loop over every entity session result and check for fastest lap in class
            foreach (var result in sim.Session.Current.Results)
            {
                // Get the class ID of this driver
                var classId = result.Entity.Car.Class.Id;

                // Get the currently recorded fastest lap for this driver's class
                // Note: the lap may not exist yet, in that case we add an entry for this class but keep the lap as null.
                ILap currentFastLap = null;
                if (_fastlaps.ContainsKey(classId))
                    currentFastLap = _fastlaps[classId];
                else
                    _fastlaps.Add(classId, null);

                // Get fastest lap of this driver
                var lap = result.FastestLap;
                
                // Check if the driver fastlap is faster than the currently stored fast lap
                // if there is no lap stored, this is a new lap and we consider it the fastest lap so far
                if (currentFastLap == null || currentFastLap.Time > lap.Time) 
                {
                    // "lap" is the newest fast lap of this class
                    // Replace it as the latest fast lap
                    _fastlaps[classId] = lap;

                    // Now trigger whatever you want to do, e.g. show a widget.
                    TriggerFastLap(item.Theme, result, lap);                        
                }
            }
        }

        private void TriggerFastLap(ITheme theme, IEntitySessionResult result, ILap lap)
        {
            // Just an example: find a widget named after the class ID
            var classId = result.Entity.Car.Class.Id;
            var widgetName = "Wigdet_Fastlap_Class" + classId;
            var widget = theme.Widgets.Find(widgetName);
            if (widget != null)
            {
                widget.Show();

                // Maybe start a timer that hides all fastlap widgets after few seconds
                var timer = theme.Timers.Find("FastLapHideTimer");
                timer.Start();
            }
            else 
            {
                // Log that this widget doesnt exist
                Console.WriteLine("Failed to find widget " + widgetName);
            }
        }
    }
}

I test it and run it with some modifications to do it ok.

In the code you use

  • var classId = result.Entity.Car.Class.Id;
    it return a Class number and it different in each series I think, for exemple it return 100 for LMP1

If I change to var classId = result.Entity.Car.Class.Order; to get 0/1/2/.... I have a message with Can not convert from "int" to "byte" ! for this line for exemple:
if (_fastlaps.ContainsKey(classId))
currentFastLap = _fastlaps;

A solution ?

Emmanuel S.
Reply #21

Find, sorry :)

var classOrder = result.Entity.Car.Class.Order;
byte classId= Convert.ToByte(classOrder);

Nick Thissen Appgineering
Reply #22
· edited

Ah, yes, you need Order not Id.

Byte is just a smaller version of an integer and only accepts values 0-255. Just change byte to int in the declaration of the _fastlaps dictionary:

Dictionary<byte, ILap>
change to
Dictionary<int, ILap>

Archive · Read-only

New replies have moved to Discord.