/// <summary> /// Start multiple sessions in one run continuously. /// </summary> /// <param name="subject">subject name</param> /// <param name="sessionDescriptors">descriptors of each session</param> /// <param name="paradigms">paradigms of each session</param> /// <param name="devices">devices used across sessions</param> /// <param name="sessionListener"></param> public static void StartSessions(string subject, [NotNull] string[] sessionDescriptors, [NotNull] SerializedObject[] paradigms, [CanBeNull] DeviceConfig[] devices, ISessionListener sessionListener = null) { subject = subject?.Trim2Null() ?? throw new UserException("subject name cannot be empty"); if (sessionDescriptors == null) { throw new ArgumentNullException(nameof(sessionDescriptors)); } if (paradigms == null) { throw new ArgumentNullException(nameof(paradigms)); } if (sessionDescriptors.Length != paradigms.Length) { throw new ProgrammingException("The count of session descriptors and the count of paradigms are not equal"); } for (var i = 0; i < sessionDescriptors.Length; i++) { sessionDescriptors[i] = sessionDescriptors[i]?.Trim2Null() ?? throw new UserException("session descriptor name cannot be empty"); } var sessionNum = sessionDescriptors.Length; /* Constructs paradigm instances. */ var paradigmInstances = new IParadigm[sessionNum]; var formattedSessionDescriptors = new string[sessionNum]; for (var i = 0; i < sessionNum; i++) { var paradigm = paradigms[i]; if (!App.Instance.Registries.Registry <ParadigmTemplate>().LookUp(paradigm.Id, out var paradigmTemplate)) { throw new ArgumentException($"Cannot find specific paradigm by id: {paradigm.Id}"); } var paradigmContext = paradigmTemplate.DeserializeArgs(paradigm.Args); if (!TryInitiateParadigm(paradigmTemplate, paradigmContext, out var paradigmInstance)) { return; } paradigmInstances[i] = paradigmInstance; formattedSessionDescriptors[i] = SessionConfigExt.StringInterpolation(sessionDescriptors[i], paradigmContext); } var deviceTypeMap = App.Instance.Registries.Registry <DeviceTypeAddOn>().Registered.Select(pdt => pdt.DeviceType).ToDictionary(dt => dt.Name); /* Constructs device map and consumers map. */ var deviceMap = new Dictionary <DeviceType, DeviceConfig>(); var deviceConsumerLists = new Dictionary <DeviceType, IList <TemplateWithArgs <ConsumerTemplate> > >(); if (devices != null) { foreach (var deviceConf in devices) { if (deviceTypeMap.TryGetValue(deviceConf.DeviceType, out var deviceType) && !deviceMap.ContainsKey(deviceType)) { deviceMap[deviceType] = deviceConf; var list = new List <TemplateWithArgs <ConsumerTemplate> >(); deviceConsumerLists[deviceType] = list; if (deviceConf.Consumers == null) { continue; } foreach (var consumerConf in deviceConf.Consumers) { if (consumerConf.Id == null) { continue; } if (!App.Instance.Registries.Registry <ConsumerTemplate>().LookUp(consumerConf.Id, out var consumerTemplate)) { throw new ArgumentException($"Cannot find specific consumer by id: {consumerConf.Id}"); } list.Add(new TemplateWithArgs <ConsumerTemplate>(consumerTemplate, consumerTemplate.DeserializeArgs(consumerConf.Args))); } } } } /* IMPORTANT: ALL PARADIGM RELATED CONFIG SHOULD BE CHECKED BEFORE STEAMERS WERE INITIALIZED */ var deviceInstances = InitiateDevices(deviceTypeMap.Values, deviceMap); var sessions = new Session[sessionNum]; var baseClock = Clock.SystemMillisClock; var streamers = CreateStreamerCollection(deviceTypeMap.Values, deviceInstances, baseClock, out var deviceStreamers); sessionListener?.BeforeAllSessionsStart(); var disposablePool = new DisposablePool(); try { streamers.Start(); for (var i = 0; i < sessionNum; i++) { var session = sessions[i] = new Session(App.Instance.Dispatcher, subject, formattedSessionDescriptors[i], baseClock, paradigmInstances[i], streamers, App.DataDir); new SessionConfig { Subject = subject, SessionDescriptor = sessionDescriptors[i], Paradigm = paradigms[i], Devices = devices } .JsonSerializeToFile(session.GetDataFileName(SessionConfig.FileSuffix), JsonUtils.PrettyFormat, Encoding.UTF8); foreach (var entry in deviceConsumerLists) { if (deviceStreamers.TryGetValue(entry.Key, out var deviceStreamer)) { var consumerListOfDevice = entry.Value; var indexed = consumerListOfDevice.Count > 1; byte num = 1; foreach (var consumerWithParams in consumerListOfDevice) { System.Diagnostics.Debug.Assert(consumerWithParams.Template != null, "consumerWithParams.Template != null"); var consumer = consumerWithParams.Template.NewInstance(session, consumerWithParams.Args, indexed ? num++ : (byte?)null); disposablePool.AddIfDisposable(consumer); deviceStreamer.AttachConsumer(consumer); disposablePool.Add(new DelegatedDisposable(() => deviceStreamer.DetachConsumer(consumer))); } } } sessionListener?.BeforeSessionStart(i, session); var result = session.Run(); sessionListener?.AfterSessionCompleted(i, session); disposablePool.DisposeAll(); // Release resources. new SessionInfo(session).JsonSerializeToFile(session.GetDataFileName(SessionInfo.FileSuffix), JsonUtils.PrettyFormat, Encoding.UTF8); result?.Save(session); if (session.UserInterrupted && i < sessionNum - 1 && MessageBox.Show("Continue following sessions?", "User Exit", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No, MessageBoxOptions.None) == MessageBoxResult.No) { break; } } } finally { disposablePool.Dispose(); streamers.Stop(); } foreach (var instance in deviceInstances.Values) { instance.Dispose(); } sessionListener?.AfterAllSessionsCompleted(sessions); }