diff --git a/MAT.SQLRace.BaseExampleLoadSSN/Program.cs b/MAT.SQLRace.BaseExampleLoadSSN/Program.cs index 637ae71..277a6ac 100644 --- a/MAT.SQLRace.BaseExampleLoadSSN/Program.cs +++ b/MAT.SQLRace.BaseExampleLoadSSN/Program.cs @@ -69,7 +69,7 @@ private static void ReadSamples(IClientSession clientSession) private static IClientSession LoadSession(string pathToFile) { var fileSessionManager = FileSessionManager.CreateFileSessionManager(); - var clientSession = fileSessionManager.Load(pathToFile); // session with associates + var clientSession = fileSessionManager.Load(pathToFile); if (clientSession == null) { diff --git a/MAT.SQLRace.HelloData/FunctionHelper.cs b/MAT.SQLRace.HelloData/FunctionHelper.cs index 8693acc..ba06936 100644 --- a/MAT.SQLRace.HelloData/FunctionHelper.cs +++ b/MAT.SQLRace.HelloData/FunctionHelper.cs @@ -2,6 +2,7 @@ // Copyright (c) Motion Applied Ltd. using System; +using System.Text; using MESL.SqlRace.Domain.Functions; using MESL.SqlRace.Domain.Functions.Fdl; @@ -51,5 +52,70 @@ public static IFunctionDefinition CreateFdlFunctionDefinition(IFunctionManager f return functionDefinition; } + + public static IFunctionDefinition CreateFdlFunctionDefinition( + IFunctionManager functionManager, + string[] parameterIdentifiers, + FunctionMode functionMode = FunctionMode.Instantaneous) + { + if (parameterIdentifiers == null || + parameterIdentifiers.Length == 0) + { + throw new InvalidOperationException("Parameters expected not to be empty."); + } + + const string FunctionName = "ParametersCombined"; + + //Create function + var parameterCode = new StringBuilder(); + for (var i = 0; i < parameterIdentifiers.Length; i++) + { + var parameterIdentifier = parameterIdentifiers[i]; + + parameterCode.Append("$"); + parameterCode.Append(parameterIdentifier); + + if (i < parameterIdentifiers.Length - 1) + { + parameterCode.Append(" + "); + } + } + + var parametersDoubled = $"return ({parameterCode})"; + + var functionDefinition = functionManager.CreateFunctionDefinition(FdlFunctionConstants.UniqueId); + + // The implementation of a function is completely agnostic. We requested a function definition from + // the FDL runtime, so the generic function implementation definition in the definition returned can + // be safely cast to the more specific FDL implementation definition. + var fdlFunctionImplementationDefinition = (IFdlFunctionImplementationDefinition)functionDefinition.ImplementationDefinition; + fdlFunctionImplementationDefinition.FunctionCode = parametersDoubled; + + // FDL doesn't define the output for a single parameter - it's simply the return value of the + // function - so we have to tell the function executor what to do with the output it generates + var outputParameter = FunctionOutputParameterDefinition + .Create($"{FunctionName}", "FunctionParameters", "Parameters Combined") + .Units("kph") + .FormatOverride("%.2f") + .MinimumValue("0.0") + .MaximumValue("700.0"); + + functionDefinition.OutputParameterDefinitions.Add(outputParameter); + + // Functions in ATLAS 9 have a number of different properties that define how, where, when and on + // what a function executes. ATLAS 10 will add more of these properties. Each value here is the + // default for its property, shown for illustration. + functionDefinition.Name = $"{FunctionName}"; + functionDefinition.FunctionMode = functionMode; + functionDefinition.CalculationModeInfoDefinition.Mode = CalculationMode.EachSamplePoint; + functionDefinition.InterpolateBetweenSamples = false; + functionDefinition.JoinGapsAroundNull = true; + functionDefinition.CalculateOverWholeSession = false; + functionDefinition.StoreInSession = false; + functionDefinition.ShouldHide = false; + functionDefinition.ShouldPersist = false; + + return functionDefinition; + } } } \ No newline at end of file diff --git a/MAT.SQLRace.HelloData/Program.cs b/MAT.SQLRace.HelloData/Program.cs index 9e4c92e..a585df8 100644 --- a/MAT.SQLRace.HelloData/Program.cs +++ b/MAT.SQLRace.HelloData/Program.cs @@ -2,15 +2,19 @@ // Copyright (c) Motion Applied Ltd. using MAT.OCS.Core; + using MESL.SqlRace.Common.Extensions; using MESL.SqlRace.Domain; using MESL.SqlRace.Domain.Collections; +using MESL.SqlRace.Domain.Events; using MESL.SqlRace.Domain.Functions; +using MESL.SqlRace.Domain.Infrastructure.DataPipeline; using MESL.SqlRace.Domain.Infrastructure.Enumerators; using MESL.SqlRace.Domain.Query; using MESL.SqlRace.Enumerators; +using MESL.SqlRace.Functions.Interfaces.Enums; using MESL.SqlRace.UI; -using MESL.SqlRace.Domain.Events; + using System; using System.Collections.Generic; using System.Diagnostics; @@ -18,9 +22,6 @@ using System.Threading; using System.Threading.Tasks; -using MESL.SqlRace.Domain.Infrastructure.DataPipeline; -using MESL.SqlRace.Functions.Interfaces.Enums; - namespace MAT.SQLRace.HelloData { internal class Program @@ -57,24 +58,27 @@ private static void Main(string[] args) //LapStatistics(); //ConnectionManagerTest(); //CreateSessionAndAddData(); - //CompositeSessionTestWithAppendedSessions(); + //AppendSessions(); //CompositeSessionWithvTagSessionsTest(); //AddInMemoryParameterToSession(); //AddParameterToExistingSession(); //ChannelStats(); //Benchmark(); - //LoadAssociated(); + //GetAssociatedSessionSummaries(); - //LoadSSNWithAssociatedMerge(); //AddEvents(); //LoadLiveSessionAndWaitForLapEvents(); //LoadLiveSessionAndWaitForEvents(); //LoadLiveSamples(); //AddAndProcessMarkers(); LoadSSN(); + //LoadSSNWtihAssociates(); + //LoadSqlRaceDatabaseSessionWithAssociates(); + //LoadSessionViaQueryManagerWithAssociate(); //GetSessionSummaryBySessionGUID(); //LoadLiveFunction(); + //CreateFunctionAcrossMultipleSessionsWithCompositeSession(); //WholeSessionsCompareMode(); //TestMaxAndMinSampleIntervals(); @@ -283,28 +287,31 @@ private static long[] CreateTimestampsForTimerange(long startTime, long endTime, } /// - /// Loads a session with an associated session + /// This example demonstrates how to get associated session summaries for a given session + /// from a SQL backed session database. It retrieves the list of associated sessions + /// and prints their identifiers. + /// + /// This is particularly useful in situations where you know you want to load associate + /// sessions but do not yet know which associates are available. The returned summaries + /// can be inspected to decide which associates to load or process next. /// - private static void LoadAssociated() + private static void GetAssociatedSessionSummaries() { - ConnectionString = @"Data Source=mesltgs1;Initial Catalog=SQLRACE143;Integrated Security=True"; + // ========= EDITABLE SECTION (tweak these values for your run) ========= + const string connectionString = @""; + const string sessionKeyValue = ""; + // ===================================================================== - string parameterIdentifier = "BAeroAutoZero:MRLAero"; - SessionKey sessionKey = new SessionKey("3FA70D1D-F526-4A10-9396-589B3BE23625"); + ConnectionString = connectionString; - var sessionManager = SessionManager.CreateSessionManager(); - var sessionSummaries = sessionManager.LoadAssociatesForSession(sessionKey, ConnectionString); - var additionalSessionKey = sessionSummaries.OrderBy(ss => ss.TimeOfRecording).Last(); - IClientSession clientSession = sessionManager.Load(sessionKey, ConnectionString, new[] { additionalSessionKey.Key }); + var sessionKey = new SessionKey(sessionKeyValue); - var containsChannel = clientSession.Session.ContainsParameter(parameterIdentifier); - Console.WriteLine( - $"Parameter {parameterIdentifier} exists in session: {containsChannel}"); + var sessionManager = SessionManager.CreateSessionManager(); - using (var pda = clientSession.Session.CreateParameterDataAccess(parameterIdentifier)) + var sessionSummaries = sessionManager.LoadAssociatesForSession(sessionKey, ConnectionString); + foreach (var sessionSummary in sessionSummaries) { - Console.WriteLine( - $"PDA {parameterIdentifier} successfully created"); + Console.WriteLine($"{sessionSummary.Identifier}"); } } @@ -875,31 +882,31 @@ private static void AddAndProcessMarkers() } /// - /// Load data from an SSN file + /// This example demonstrates how to load a .ssn file using the FileSessionManager, + /// and then request and print sample data for a given Parameter Identifier. /// private static void LoadSSN() { - var fileSessionManager = FileSessionManager.CreateFileSessionManager(); + // ========= EDITABLE SECTION (tweak these values for your run) ========= + const string ssnPath = @""; + const string parameterIdentifier = ""; + // ===================================================================== - //var session01 = fileSessionManager.Load(@"C:\Session Location\Session To Load.ssn"); - var session01 = fileSessionManager.Load(@"C:\Session Location\Session To Load.ssn", new List - { - @"C:\Session Location\Session To Load.VTS.001.ssv" - }); // session with associates + var fileSessionManager = FileSessionManager.CreateFileSessionManager(); - if (session01 == null) + var session = fileSessionManager.Load(ssnPath); + if (session == null) { - Console.WriteLine("Session not found"); + Console.WriteLine("Session not found."); return; } - - var vCarIdentifier = "vCar:Chassis"; - using (var pda = session01.Session.CreateParameterDataAccess(vCarIdentifier)) + + using (var pda = session.Session.CreateParameterDataAccess(parameterIdentifier)) { - pda.GoTo(session01.Session.StartTime + (session01.Session.EndTime - session01.Session.StartTime) / 2); + pda.GoTo(session.Session.StartTime + (session.Session.EndTime - session.Session.StartTime) / 2); var samples = pda.GetNextSamples(10); - Console.WriteLine($"** Data for {vCarIdentifier}"); + Console.WriteLine($"** Data for {parameterIdentifier}"); for (var i = 0; i < samples.SampleCount; i++) { Console.WriteLine($"{samples.Timestamp[i].ToTimeString()} {samples.Data[i]}"); @@ -907,54 +914,108 @@ private static void LoadSSN() } } - private static void LoadSSNWithAssociatedMerge() + /// + /// This example loads a Primary session and an Associate session in order to + /// create a Composite Session (a session made up of one or more sessions). + /// It then retrieves and prints a small number of sample values for a + /// specified parameter from both the Primary and the Associate sessions. + /// + private static void LoadSSNWtihAssociates() { - //SQLite ssn style connection string - ConnectionString = $@"DbEngine=SQLite;Data Source={ - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) - }\McLaren Applied Technologies\ATLAS 10\SQL Race\IndexingSessionCache.ssn2;"; + // ========= EDITABLE SECTION (tweak these values for your run) ========= + const string primarySsnPath = @""; + const string associateSsnPath = @""; + const string primaryParamId = ""; + const string associateParamId = ""; + // ===================================================================== - var qm = QueryManager.CreateQueryManager(ConnectionString); - qm.SearchOptions = SearchOptions.RetrieveAssociateSessionSummaries; - // Get the most recent live session from the database - var sessionSummaries = qm.ExecuteQuery().ToList(); + var fileSessionManager = FileSessionManager.CreateFileSessionManager(); - var sessionSummary01 = - sessionSummaries.FirstOrDefault( - x => x.Identifier == ""); - if (sessionSummary01 == null) + var compositeSession = fileSessionManager.LoadCompositeSession(primarySsnPath,new List { associateSsnPath }); + + if (compositeSession == null) { - Console.WriteLine("Session not found"); + Console.WriteLine("Failed to create composite session."); return; } - Console.WriteLine("Loading session..."); + using (var pda = compositeSession.CreateParameterDataAccess(primaryParamId)) + { + pda.GoTo(compositeSession.StartTime + (compositeSession.EndTime - compositeSession.StartTime) / 2); + var samples = pda.GetNextSamples(10); + + Console.WriteLine($"** Data for {primaryParamId}"); + for (var i = 0; i < samples.SampleCount; i++) + { + Console.WriteLine($"{samples.Timestamp[i].ToTimeString()} {samples.Data[i]}"); + } + } + + using (var pda = compositeSession.CreateParameterDataAccess(associateParamId)) + { + pda.GoTo(compositeSession.StartTime + (compositeSession.EndTime - compositeSession.StartTime) / 2); + var samples = pda.GetNextSamples(10); + + Console.WriteLine($"** Data for {associateParamId}"); + for (var i = 0; i < samples.SampleCount; i++) + { + Console.WriteLine($"{samples.Timestamp[i].ToTimeString()} {samples.Data[i]}"); + } + } + } + + /// + /// This example demonstrates how to load a Primary session and an Associate session + /// from a SQL backed session database in order to create a Composite Session + /// (a session made up of one or more sessions). It then retrieves and prints + /// a small number of sample values for a specified parameter from both the + /// Primary session and its Associate. + /// + private static void LoadSqlRaceDatabaseSessionWithAssociates() + { + // ========= EDITABLE SECTION (tweak these values for your run) ========= + const string connectionString = @""; + const string primarySessionKey = ""; + const string associateSessionKey = ""; + const string sessionParamId = ""; + const string associateParamId = ""; + // ===================================================================== + + ConnectionString = connectionString; + var sessionKey = new SessionKey(primarySessionKey); + var associateKey = new SessionKey(associateSessionKey); + var sessionManager = SessionManager.CreateSessionManager(); - var session01 = sessionManager.Load(sessionSummary01.Key, ConnectionString, new[] { sessionSummary01.Associates.Last() }); - var session = session01.Session; + Console.WriteLine("Loading session..."); + var compositeSession = sessionManager.LoadCompositeSession( + sessionKey, + ConnectionString, + [associateKey]); Console.WriteLine("Session loaded"); - var muTyreRLIdentifier = "muTyreRL:MRLTyres"; - using (var pda = session01.Session.CreateParameterDataAccess(muTyreRLIdentifier)) + using (var pda = compositeSession.CreateParameterDataAccess(sessionParamId)) { - pda.GoTo(session.StartTime + (session.EndTime - session.StartTime) / 2); + pda.GoTo(compositeSession.StartTime + + (compositeSession.EndTime - compositeSession.StartTime) / 2); + var samples = pda.GetNextSamples(10); - Console.WriteLine($"** Data for {muTyreRLIdentifier}"); + Console.WriteLine($"** Data for {sessionParamId}"); for (var i = 0; i < samples.SampleCount; i++) { Console.WriteLine($"{samples.Timestamp[i].ToTimeString()} {samples.Data[i]}"); } } - var vCarIdentifier = "vCar:Chassis"; - using (var pda = session01.Session.CreateParameterDataAccess(vCarIdentifier)) + using (var pda = compositeSession.CreateParameterDataAccess(associateParamId)) { - pda.GoTo(session.StartTime + (session.EndTime - session.StartTime) / 2); + pda.GoTo(compositeSession.StartTime + + (compositeSession.EndTime - compositeSession.StartTime) / 2); + var samples = pda.GetNextSamples(10); - Console.WriteLine($"** Data for {vCarIdentifier}"); + Console.WriteLine($"** Data for {associateParamId}"); for (var i = 0; i < samples.SampleCount; i++) { Console.WriteLine($"{samples.Timestamp[i].ToTimeString()} {samples.Data[i]}"); @@ -962,60 +1023,71 @@ private static void LoadSSNWithAssociatedMerge() } } - private static void CompositeSessionTestWithAppendedSessions() + /// + /// This example demonstrates how to use QueryManager to retrieve session summaries, + /// select a specific session by Identifier, and then load that session together with + /// one of its associates as a Composite Session. It then retrieves and prints a small + /// number of sample values for a specified parameter from both the Primary session + /// and its Associate. + /// + private static void LoadSessionViaQueryManagerWithAssociate() { - ConnectionString = @"Data Source=mesltgs1;Initial Catalog=SQLRACE143;Integrated Security=True"; + // ========= EDITABLE SECTION (tweak these values for your run) ========= + const string connectionString = @""; + const string sessionIdentifier = ""; + const string sessionParamId = ""; + const string associateParamId = ""; + // ===================================================================== - var compositeSession = new CompositeSession(CompositeSessionKey.NewKey(), "CompositeSessionWithvTagSessionsTest"); + ConnectionString = connectionString; var qm = QueryManager.CreateQueryManager(ConnectionString); - // Get the most recent live session from the database - var sessionSummaries = qm.ExecuteQuery().ToList(); + qm.SearchOptions = SearchOptions.RetrieveAssociateSessionSummaries; - var sessionSummary01 = - sessionSummaries.FirstOrDefault( - x => x.Identifier == ""); - if (sessionSummary01 == null) + // Discover sessions in the store and find the one we want by human friendly Identifier + var sessionSummaries = qm.ExecuteQuery().ToList(); + var sessionSummary = sessionSummaries.FirstOrDefault(x => x.Identifier == sessionIdentifier); + if (sessionSummary == null) { Console.WriteLine("Session not found"); return; } - var sessionSummary02 = - sessionSummaries.FirstOrDefault( - x => x.Identifier == ""); - if (sessionSummary02 == null) + if (sessionSummary.Associates == null || sessionSummary.Associates.Count == 0) { - Console.WriteLine("Session not found"); + Console.WriteLine("No associates found for the selected session."); return; } var sessionManager = SessionManager.CreateSessionManager(); - Console.WriteLine("Loading sessions..."); - - var session01 = sessionManager.Load(sessionSummary01.Key, ConnectionString); - Console.WriteLine($"Session 1 loaded (start: {session01.Session.StartTime.ToTimeString()} end: {session01.Session.EndTime.ToTimeString()}"); - - var session02 = sessionManager.Load(sessionSummary02.Key, ConnectionString); - Console.WriteLine($"Session 2 loaded (start: {session02.Session.StartTime.ToTimeString()} end: {session02.Session.EndTime.ToTimeString()}"); + Console.WriteLine("Loading session..."); + var compositeSession = sessionManager.LoadCompositeSession( + sessionSummary.Key, + ConnectionString, + [sessionSummary.Associates.Last()]); + Console.WriteLine("Session loaded"); - compositeSession.Add(session01); - compositeSession.Add(session02); + var midpoint = compositeSession.StartTime + (compositeSession.EndTime - compositeSession.StartTime) / 2; - var parametervCar = session01.Session.GetParameter("vCar:Chassis"); - if (parametervCar == null) + using (var pda = compositeSession.CreateParameterDataAccess(sessionParamId)) { - Console.WriteLine("Parameter not found"); - return; + pda.GoTo(midpoint); + var samples = pda.GetNextSamples(10); + + Console.WriteLine($"** Data for {sessionParamId}"); + for (var i = 0; i < samples.SampleCount; i++) + { + Console.WriteLine($"{samples.Timestamp[i].ToTimeString()} {samples.Data[i]}"); + } } - using (var pda = compositeSession.CreateParameterDataAccess(parametervCar.Identifier)) + using (var pda = compositeSession.CreateParameterDataAccess(associateParamId)) { - pda.GoTo(compositeSession.StartTime); - var samples = pda.GetNextSamples(compositeSession.EndTime); + pda.GoTo(midpoint); + var samples = pda.GetNextSamples(10); - Console.WriteLine($"** Data for {parametervCar.Identifier}"); + Console.WriteLine($"** Data for {associateParamId}"); for (var i = 0; i < samples.SampleCount; i++) { Console.WriteLine($"{samples.Timestamp[i].ToTimeString()} {samples.Data[i]}"); @@ -1023,33 +1095,42 @@ private static void CompositeSessionTestWithAppendedSessions() } } - private static void CompositeSessionWithvTagSessionsTest() + /// + /// This example demonstrates how to load two individual sessions from a SQL backed + /// session database, append them into a Composite Session, and then request and + /// print sample data for a specified parameter across the combined session range. + /// + private static void AppendSessions() { - //SQLite ssn style connection string - ConnectionString = @"Data Source=mesltgs1;Initial Catalog=SQLRACE143;Integrated Security=True"; + // ========= EDITABLE SECTION (tweak these values for your run) ========= + const string connectionString = @""; + const string sessionIdentifier1 = ""; + const string sessionIdentifier2 = ""; + const string parameterIdentifier = ""; + // ===================================================================== - var compositeSession = new CompositeSession(CompositeSessionKey.NewKey(), "CompositeSessionWithvTagSessionsTest"); + ConnectionString = connectionString; + var compositeSession = new CompositeSession( + CompositeSessionKey.NewKey(), + "CompositeSessionWithvTagSessionsTest"); var qm = QueryManager.CreateQueryManager(ConnectionString); - // Get the most recent live session from the database + + // Get the most recent live sessions from the database var sessionSummaries = qm.ExecuteQuery().ToList(); - var sessionSummary01 = - sessionSummaries.FirstOrDefault( - x => x.Identifier == ""); + var sessionSummary01 = sessionSummaries.FirstOrDefault(x => x.Identifier == sessionIdentifier1); if (sessionSummary01 == null) { - Console.WriteLine("Session not found"); + Console.WriteLine("Session 1 not found"); return; } - var sessionSummary02 = - sessionSummaries.FirstOrDefault( - x => x.Identifier == ""); + var sessionSummary02 = sessionSummaries.FirstOrDefault(x => x.Identifier == sessionIdentifier2); if (sessionSummary02 == null) { - Console.WriteLine("Session not found"); + Console.WriteLine("Session 2 not found"); return; } @@ -1058,46 +1139,29 @@ private static void CompositeSessionWithvTagSessionsTest() Console.WriteLine("Loading sessions..."); var session01 = sessionManager.Load(sessionSummary01.Key, ConnectionString); - Console.WriteLine("Session loaded"); + Console.WriteLine($"Session 1 loaded (start: {session01.Session.StartTime.ToTimeString()} end: {session01.Session.EndTime.ToTimeString()})"); var session02 = sessionManager.Load(sessionSummary02.Key, ConnectionString); - Console.WriteLine("Associated SSV loaded"); + Console.WriteLine($"Session 2 loaded (start: {session02.Session.StartTime.ToTimeString()} end: {session02.Session.EndTime.ToTimeString()})"); compositeSession.Add(session01); compositeSession.Add(session02); - var parameterAero = session02.Session.GetParameter("muTyreRL:MRLTyres"); - if (parameterAero == null) + var parameter = session01.Session.GetParameter(parameterIdentifier); + if (parameter == null) { Console.WriteLine("Parameter not found"); return; } - using (var pda = compositeSession.CreateParameterDataAccess(parameterAero.Identifier)) + using (var pda = compositeSession.CreateParameterDataAccess(parameter.Identifier)) { - pda.GoTo(compositeSession.StartTime + (compositeSession.EndTime - compositeSession.StartTime) / 2); - var samples = pda.GetNextSamples(10); - - Console.WriteLine($"** Data for {parameterAero.Identifier}"); - for (var i = 0; i < samples.SampleCount; i++) - { - Console.WriteLine($"{samples.Timestamp[i].ToTimeString()} {samples.Data[i]}"); - } - } - - var parametervCar = session01.Session.GetParameter("vCar:Chassis"); - if (parametervCar == null) - { - Console.WriteLine("Parameter not found"); - return; - } + pda.GoTo(compositeSession.StartTime); - using (var pda = compositeSession.CreateParameterDataAccess(parametervCar.Identifier)) - { - pda.GoTo(compositeSession.StartTime + (compositeSession.EndTime - compositeSession.StartTime) / 2); - var samples = pda.GetNextSamples(10); + // Request all samples until the end of the composite session + var samples = pda.GetNextSamples(compositeSession.EndTime); - Console.WriteLine($"** Data for {parametervCar.Identifier}"); + Console.WriteLine($"** Data for {parameter.Identifier}"); for (var i = 0; i < samples.SampleCount; i++) { Console.WriteLine($"{samples.Timestamp[i].ToTimeString()} {samples.Data[i]}"); @@ -1389,6 +1453,96 @@ private static void ReadParameterUnit() Console.WriteLine(); } + + /// + /// This example demonstrates how to build and evaluate a Function that uses parameters + /// originating from different sessions when they are loaded together as a Composite Session + /// (a session made up of multiple sessions). It loads a primary session together with an + /// associate session to form a Composite Session, then creates a Function definition that + /// combines the specified parameters. ParameterDataAccess (PDA) objects are created + /// for the primary parameter, the associate parameter, and the Function itself. Each PDA is + /// used to retrieve samples. The example prints both the raw parameter values and the + /// resulting Function values to the console. + /// + private static void CreateFunctionAcrossMultipleSessionsWithCompositeSession() + { + // ========= EDITABLE SECTION (tweak these values for your run) ========= + const string connectionString = @""; + const string primarySessionKey = ""; + const string associateSessionKey = ""; + const string primarySessionParameterIdentifier = ""; + const string associateParameterIdentifier = ""; + // ===================================================================== + + ConnectionString = connectionString; + var sessionKey = new SessionKey(primarySessionKey); + var associateKey = new SessionKey(associateSessionKey); + + var sessionManager = SessionManager.CreateSessionManager(); + + Console.WriteLine("Loading session..."); + var compositeSession = sessionManager.LoadCompositeSession( + sessionKey, + ConnectionString, + [associateKey]); + Console.WriteLine("Session loaded"); + + // Create & Build Function + var functionManager = FunctionManagerFactory.Create(); + var functionDefinition = FunctionHelper.CreateFdlFunctionDefinition( + functionManager, + [ + primarySessionParameterIdentifier, + associateParameterIdentifier + ]); + + var functionIdentifier = functionDefinition.OutputParameterDefinitions.First().Identifier; + var functionPda = compositeSession.CreateParameterDataAccess(functionIdentifier); + + // Get Data + var startTime = compositeSession.PrimarySession.LapCollection.FastestLap.StartTime; + + functionPda.GoTo(startTime); + var functionSamples = functionPda.GetNextSamples(5); + if (functionSamples == null) + { + Console.WriteLine($"** {functionIdentifier} Failed to get function samples **"); + return; + } + + var primarySessionParameterPda = compositeSession.CreateParameterDataAccess(primarySessionParameterIdentifier); + var primarySessionSamples = primarySessionParameterPda.GetNextSamples(5); + if (primarySessionSamples == null) + { + Console.WriteLine($"** {primarySessionParameterIdentifier} Failed to get function samples **"); + return; + } + + var associateParameterIdentifierPda = compositeSession.CreateParameterDataAccess(associateParameterIdentifier); + var associateParameterSamples = associateParameterIdentifierPda.GetNextSamples(5); + if (associateParameterSamples == null) + { + Console.WriteLine($"** {associateParameterIdentifier} Failed to get function samples **"); + return; + } + + // Print results + // Header + Console.WriteLine(" i | P | A | F | F == P + A"); + Console.WriteLine("----+-------+-------+-------+------------"); + + // Body + for (var i = 0; i < primarySessionSamples.Data.Length; i++) + { + var ok = functionSamples.Data[i] == primarySessionSamples.Data[i] + associateParameterSamples.Data[i]; + Console.WriteLine( + $"{i,2} | {primarySessionSamples.Data[i],5} | {associateParameterSamples.Data[i],5} | {functionSamples.Data[i],5} | {(ok ? "✓" : "✗")} ({functionSamples.Data[i]} {(ok ? "=" : "≠")} {primarySessionSamples.Data[i]}+{associateParameterSamples.Data[i]})" + ); + } + + Console.WriteLine(""); + } + private static void Session_LapStarted(object sender, LapEventArgs e) { Console.WriteLine($"Lap started {e.Lap.Name}");