Fast lap of each class?

Posts: 29
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?
Posts: 785
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);
}
}
}
}
Posts: 287
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 ?
Posts: 785
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.
Posts: 287
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 ?
Posts: 785
Sorry, I don't understand your question.
Posts: 287
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 ?
Edited (2 times)
Posts: 785
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 IEntity will actually be of type ITeam 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 IDriver 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;
}

Edited (1 time)
Posts: 287
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;
}
}}

Edited (2 times)
Posts: 287
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 ?
Posts: 287
And if I return otherDrivers in a ticker when this is use :  var otherDrivers = entity.Drivers; does it show a driver list ?
Edited (4 times)
Posts: 785
Sorry I'm a bit lost on your earlier (longer) question, I don't know what you're trying to ask.

Emmanuel Suter 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 Suter 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.
Posts: 287
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.
Posts: 785
A simple check for "IsVisible" should do it, no?
Posts: 287
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;
}
}}}

Edited (1 time)
Posts: 785
My guess would be there is no widget named "W_Pilote_Complet", so 'widget0' is null.
Posts: 287
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.
Posts: 785
Weird, that should work just fine. Does the fading work in a normal Show widget action with a value of '500' set?
Posts: 287
Yes it works. I did not know we can add this value to fade ...
Edited (1 time)
Posts: 287
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[classId];

A solution ?
Posts: 287
Find, sorry :)

var classOrder = result.Entity.Car.Class.Order;
byte classId= Convert.ToByte(classOrder);
Posts: 785
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>
Edited (1 time)
Posts: 287
Oh, I try only Convert and now it is ok

Thanks a lot and it is always a new lesson for me ;)
Edited (1 time)