/// <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); }
public static void StartSession(IReadOnlyList <SessionConfig.Experiment> experimentParts, IDictionary <string, DeviceParams> devicePart, bool monitor = false, ISessionListener sessionListener = null) { /* Constructs experiment instances. */ var experiments = new IExperiment[experimentParts.Count]; var formattedSessionDescriptors = new string[experimentParts.Count]; for (var i = 0; i < experimentParts.Count; i++) { var expConf = experimentParts[i]; if (!App.Instance.Registries.Registry <PluginExperiment>().LookUp(expConf.Params.Id, out var pluginExperiment)) { throw new ArgumentException($"Cannot find specific experiment by id: {expConf.Params.Id}"); } if (!TryInitiateExperiment(pluginExperiment, pluginExperiment.DeserializeParams(expConf.Params.Params), out var experiment)) { return; } experiments[i] = experiment; formattedSessionDescriptors[i] = expConf.GetFormattedSessionDescriptor(); } /* Parse consumer configurations. */ var deviceConsumerLists = new Dictionary <DeviceType, IList <Tuple <PluginStreamConsumer, IReadonlyContext> > >(); foreach (var deviceType in App.Instance.Registries.Registry <PluginDeviceType>().Registered) { if (!devicePart.TryGetValue(deviceType.DeviceType.Name, out var deviceParams) || deviceParams.Device.Id == null) { continue; } var list = new List <Tuple <PluginStreamConsumer, IReadonlyContext> >(); deviceConsumerLists[deviceType.DeviceType] = list; foreach (var consumerConf in deviceParams.Consumers) { if (!App.Instance.Registries.Registry <PluginStreamConsumer>().LookUp(consumerConf.Id, out var pluginStreamConsumer)) { throw new ArgumentException($"Cannot find specific consumer by id: {consumerConf.Params}"); } list.Add(new Tuple <PluginStreamConsumer, IReadonlyContext>(pluginStreamConsumer, pluginStreamConsumer.DeserializeParams(consumerConf.Params))); } } /* IMPORTANT: ALL EXPERIMENT RELATED CONFIG SHOULD BE CHECKED BEFORE STEAMERS WERE INITIALIZED */ var monitorWindow = monitor ? MonitorWindow.Show() : null; var sessions = new Session[experimentParts.Count]; var baseClock = Clock.SystemMillisClock; var streamers = CreateStreamerCollection(devicePart, baseClock, out var deviceStreamers); using (var disposablePool = new DisposablePool()) { try { streamers.Start(); monitorWindow?.Bind(streamers); for (var i = 0; i < experimentParts.Count; i++) { var experimentPart = experimentParts[i]; var experiment = experiments[i]; var sessionName = formattedSessionDescriptors[i]; var session = sessions[i] = new Session(App.Instance.Dispatcher, experimentPart.Subject, sessionName, baseClock, experiment, streamers, App.DataDir); new SessionConfig { ExperimentPart = experimentPart, DevicePart = devicePart } .JsonSerializeToFile(session.GetDataFileName(SessionConfig.FileSuffix), JsonUtils.PrettyFormat, Encoding.UTF8); var writerBaseTime = session.CreateTimestamp; if (ExperimentProperties.RecordMarkers.Get(experiment.Metadata) && streamers.TryFindFirst <MarkerStreamer>(out var markerStreamer)) { var markerFileWriter = new MarkerFileWriter(session.GetDataFileName(MarkerFileWriter.FileSuffix), writerBaseTime); disposablePool.Add(markerFileWriter); markerStreamer.Attach(markerFileWriter); disposablePool.Add(new DelegatedDisposable(() => markerStreamer.Detach(markerFileWriter))); } foreach (var entry in deviceConsumerLists) { if (deviceStreamers.TryGetValue(entry.Key, out var deviceStreamer)) { var deviceConsumerList = entry.Value; var indexed = deviceConsumerList.Count > 1; byte num = 1; foreach (var tuple in deviceConsumerList) { var consumer = tuple.Item1.NewInstance(session, tuple.Item2, indexed ? num++ : (byte?)null); if (consumer is IDisposable disposable) { disposablePool.Add(disposable); } deviceStreamer.Attach(consumer); disposablePool.Add(new DelegatedDisposable(() => deviceStreamer.Detach(consumer))); } } } sessionListener?.BeforeStart(i, session); var result = session.Run(); sessionListener?.AfterCompleted(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 < experimentParts.Count - 1 && MessageBox.Show("Continue following sessions?", "User Exit", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No, MessageBoxOptions.None) == MessageBoxResult.No) { break; } } } finally { streamers.Stop(); monitorWindow?.Release(); // Detach session from monitor. } } sessionListener?.AfterAllCompleted(sessions); }