< Summary - pva.SuperV

Information
Class: pva.SuperV.Engine.RunnableProject
Assembly: pva.SuperV.Engine
File(s): /home/runner/work/pva.SuperV/pva.SuperV/pva.SuperV.Engine/RunnableProject.cs
Tag: dotnet-ubuntu_18869653307
Line coverage
96%
Covered lines: 216
Uncovered lines: 9
Coverable lines: 225
Total lines: 380
Line coverage: 96%
Branch coverage
82%
Covered branches: 41
Total branches: 50
Branch coverage: 82%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_ProjectAssemblyLoaderWeakRef()50%22100%
get_Instances()100%11100%
.ctor()100%11100%
.ctor(...)100%11100%
GetId()50%22100%
SetupProjectAssemblyLoader()100%22100%
CreateHistoryClassTimeSeries()100%22100%
CreateHistoryRepositories(...)100%44100%
CreateInstance(...)75%121293.33%
CreateInstance(...)100%11100%
GetConstructor(...)50%22100%
RemoveInstance(...)100%11100%
GetInstance(...)100%22100%
RecreateInstances(...)100%11100%
Unload()100%44100%
GetCode()100%11100%
SetInstanceValue(...)100%11100%
SetInstanceValue(...)100%11100%
SetInstanceValue(...)100%210%
SetInstanceValue(...)50%2275%
GetHistoryValues(...)100%11100%
GetHistoryValues(...)100%11100%
GetHistoryStatistics(...)100%22100%
GetHistoryStatistics(...)100%22100%
ValidateTimeRange(...)100%22100%
ValidateTimeRange(...)100%22100%
GetHistoryParametersForFields(...)75%8890.47%

File(s)

/home/runner/work/pva.SuperV/pva.SuperV/pva.SuperV.Engine/RunnableProject.cs

#LineLine coverage
 1using pva.Helpers.Extensions;
 2using pva.SuperV.Engine.Exceptions;
 3using pva.SuperV.Engine.HistoryRetrieval;
 4using pva.SuperV.Engine.HistoryStorage;
 5using pva.SuperV.Engine.Processing;
 6using System.Reflection;
 7using System.Runtime.CompilerServices;
 8using System.Text;
 9using System.Text.Json.Serialization;
 10
 11namespace pva.SuperV.Engine
 12{
 13    /// <summary>
 14    /// A runnable project. It allows to create new instances. However its definitions are fixed and can't be changed.
 15    /// </summary>
 16    /// <seealso cref="pva.SuperV.Engine.Project" />
 17    public class RunnableProject : Project
 18    {
 19        /// <summary>
 20        /// The project assembly loader.
 21        /// </summary>
 22        [JsonIgnore]
 23        private ProjectAssemblyLoader? projectAssemblyLoader;
 24
 25        [JsonIgnore]
 26        public WeakReference? ProjectAssemblyLoaderWeakRef
 27        {
 1328            get => projectAssemblyLoader == null
 1329                ? null
 1330                : new WeakReference(projectAssemblyLoader, trackResurrection: true);
 31        }
 32
 33        /// <summary>
 34        /// Gets the instances.
 35        /// </summary>
 36        /// <value>
 37        /// The instances.
 38        /// </value>
 39        [JsonIgnore]
 89340        public Dictionary<string, Instance> Instances { get; } = new(StringComparer.OrdinalIgnoreCase);
 41
 42        /// <summary>
 43        /// Initializes a new instance of the <see cref="RunnableProject"/> class.
 44        /// </summary>
 1345        public RunnableProject()
 1346        {
 1347        }
 48
 49        /// <summary>
 50        /// Initializes a new instance of the <see cref="RunnableProject"/> class from a <see cref="WipProject"/>.
 51        /// </summary>
 52        /// <param name="wipProject">The wip project.</param>
 12653        public RunnableProject(WipProject wipProject)
 12654        {
 12655            Name = wipProject.Name;
 12656            Version = wipProject.Version;
 12657            Classes = new(wipProject.Classes);
 12658            FieldFormatters = new(wipProject.FieldFormatters);
 12659            HistoryStorageEngineConnectionString = wipProject.HistoryStorageEngineConnectionString;
 12660            HistoryStorageEngine = wipProject.HistoryStorageEngine;
 12661            HistoryRepositories = new(wipProject.HistoryRepositories);
 12662            CreateHistoryRepositories(HistoryStorageEngine);
 12563            CreateHistoryClassTimeSeries();
 12464            SetupProjectAssemblyLoader();
 12465            RecreateInstances(wipProject);
 12466        }
 67
 68        public override string GetId()
 65369        {
 65370            return $"{Name!}";
 65371        }
 72
 73        private void SetupProjectAssemblyLoader()
 20374        {
 40675            Task.Run(async () => await ProjectBuilder.BuildAsync(this)).Wait();
 20376            projectAssemblyLoader ??= new();
 20377            projectAssemblyLoader.LoadFromAssemblyPath(GetAssemblyFileName());
 20378        }
 79
 80        private void CreateHistoryClassTimeSeries()
 12581        {
 12582            Classes.Values.ForEach(clazz =>
 34183            {
 34184                clazz.FieldDefinitions.Values.ForEach(fieldDefinition =>
 219485                {
 219486                    fieldDefinition.ValuePostChangeProcessings
 219487                        .OfType<IHistorizationProcessing>()
 219488                        .ForEach(hp =>
 241489                            hp.UpsertInHistoryStorage(Name!, clazz.Name!));
 253490                });
 46591            });
 12492        }
 93
 94        private void CreateHistoryRepositories(IHistoryStorageEngine? historyStorageEngine)
 12695        {
 12696            if (HistoryRepositories.Keys.Count == 0)
 1497            {
 1498                return;
 99            }
 112100            if (historyStorageEngine is null)
 1101            {
 1102                throw new NoHistoryStorageEngineException(Name);
 103            }
 104
 111105            HistoryRepositories.Values.ForEach(repository =>
 111106            {
 111107                repository.HistoryStorageEngine = historyStorageEngine;
 111108                repository.UpsertRepository(Name!, historyStorageEngine);
 222109            });
 125110        }
 111
 112        /// <summary>
 113        /// Creates an instance.
 114        /// </summary>
 115        /// <param name="className">Name of the class.</param>
 116        /// <param name="instanceName">Name of the instance.</param>
 117        /// <param name="addToRunningInstances">Indicates if the created instances should be added to running instances 
 118        /// <returns>The newly created instance.</returns>
 119        /// <exception cref="pva.SuperV.Engine.Exceptions.EntityAlreadyExistException"></exception>
 120        public Instance? CreateInstance(string className, string instanceName, bool addToRunningInstances = true)
 79121        {
 79122            SetupProjectAssemblyLoader();
 79123            if (Instances.ContainsKey(instanceName))
 1124            {
 1125                throw new EntityAlreadyExistException("Instance", instanceName);
 126            }
 127
 78128            Class clazz = GetClass(className);
 78129            string classFullName = $"{Name}.V{Version}.{clazz.Name}";
 78130            Type? classType = projectAssemblyLoader?.Assemblies.First()?.GetType(classFullName!);
 131
 78132            Instance? instance = CreateInstance(classType!);
 78133            if (instance is null)
 0134            {
 0135                return instance;
 136            }
 78137            instance.Name = instanceName;
 78138            instance.Class = clazz;
 78139            Class? currentClass = clazz;
 186140            while (currentClass is not null)
 108141            {
 108142                currentClass.FieldDefinitions.ForEach((k, v) =>
 534143                {
 534144                    instance!.Fields.TryGetValue(k, out IField? field);
 534145                    field!.FieldDefinition = v;
 642146                });
 108147                currentClass = currentClass.BaseClass;
 108148            }
 78149            if (addToRunningInstances)
 78150            {
 78151                Instances.Add(instanceName, instance);
 78152            }
 78153            return instance;
 78154        }
 155
 156        /// <summary>
 157        /// Creates an instance for targetType's <see cref="FieldDefinition{T}"/>.
 158        /// </summary>
 159        /// <param name="targetType">Type of the target.</param>
 160        /// <returns><see cref="IFieldDefinition"/> created instance.</returns>
 161        private static Instance CreateInstance(Type targetType)
 78162        {
 78163            var ctor = GetConstructor(targetType);
 78164            return (Instance)ctor.Invoke([]);
 78165        }
 166
 167        /// <summary>
 168        /// Gets the constructor for targetType's <see cref="FieldDefinition{T}"/>.
 169        /// </summary>
 170        /// <param name="targetType">Type of the target.</param>
 171        /// <param name="argumentType">Type of the argument.</param>
 172        /// <returns></returns>
 173        /// <exception cref="InvalidOperationException">No constructor found for FieldDefinition{targetType.Name}.</exce
 174        private static ConstructorInfo GetConstructor(Type targetType)
 78175        {
 78176            return targetType.GetConstructor(Type.EmptyTypes)
 78177                ?? throw new InvalidOperationException($"No constructor found for {targetType.Name}.");
 78178        }
 179
 180        /// <summary>
 181        /// Removes an instance by its name.
 182        /// </summary>
 183        /// <param name="instanceName">Name of the instance.</param>
 184        public void RemoveInstance(string instanceName)
 1185        {
 1186            Instances.Remove(instanceName);
 1187        }
 188
 189        /// <summary>
 190        /// Gets an instance by its name.
 191        /// </summary>
 192        /// <param name="instanceName">Name of the instance.</param>
 193        /// <returns>The <see cref="Instance"/></returns>
 194        /// <exception cref="pva.SuperV.Engine.Exceptions.UnknownInstanceException"></exception>
 195        public Instance GetInstance(string instanceName)
 208196        {
 208197            if (Instances.TryGetValue(instanceName, out var instance))
 207198            {
 207199                return instance;
 200            }
 201
 1202            throw new UnknownEntityException("Instance", instanceName);
 207203        }
 204
 205        /// <summary>
 206        /// Recreates the instances from a <see cref="WipProject"/>.
 207        /// </summary>
 208        /// <param name="wipProject">The wip project.</param>
 209        private void RecreateInstances(WipProject wipProject)
 124210        {
 124211            wipProject.ToLoadInstances
 124212                .ForEach((k, v) =>
 1213                {
 1214                    string instanceName = k;
 1215                    Instance oldInstance = v;
 1216                    Instance? newInstance = CreateInstance(oldInstance.Class.Name, instanceName);
 1217                    Dictionary<string, IField> newFields = new(newInstance!.Fields.Count);
 1218                    newInstance.Fields
 1219                        .ForEach((k1, v2) =>
 7220                        {
 7221                            string fieldName = k1;
 7222                            newFields.Add(fieldName,
 7223                                oldInstance.Fields.GetValueOrDefault(fieldName, v2));
 8224                        });
 1225                    newInstance.Fields = newFields;
 125226                });
 124227        }
 228
 229        /// <summary>
 230        /// Unloads the project. Clears all instances.
 231        /// </summary>
 232        [MethodImpl(MethodImplOptions.NoInlining)]
 233        public override void Unload()
 136234        {
 136235            Instances.Values.ForEach(instance =>
 212236                instance.Dispose());
 136237            Instances.Clear();
 136238            HistoryStorageEngine?.Dispose();
 136239            base.Unload();
 136240            Projects.Remove(GetId(), out _);
 136241            projectAssemblyLoader?.Unload();
 136242            projectAssemblyLoader = null;
 136243        }
 244
 245        /// <summary>
 246        /// Gets the C# code for generating the project's assembly with <see cref="Project.BuildAsync(WipProject)"/>.
 247        /// </summary>
 248        /// <returns>C# code.</returns>
 249        public string GetCode()
 124250        {
 124251            StringBuilder codeBuilder = new();
 124252            codeBuilder.AppendLine($"using {GetType().Namespace};");
 124253            codeBuilder.AppendLine("using System.Collections.Generic;");
 124254            codeBuilder.AppendLine("using System.Reflection;");
 124255            codeBuilder.AppendLine($"[assembly: AssemblyProduct(\"{Name}\")]");
 124256            codeBuilder.AppendLine($"[assembly: AssemblyTitle(\"{Description}\")]");
 124257            codeBuilder.AppendLine($"[assembly: AssemblyVersion(\"{Version}\")]");
 124258            codeBuilder.AppendLine($"[assembly: AssemblyFileVersion(\"{Version}\")]");
 124259            codeBuilder.AppendLine($"[assembly: AssemblyInformationalVersion(\"{Version}\")]");
 124260            codeBuilder.AppendLine($"namespace {Name}.V{Version} {{");
 124261            Classes
 463262                .ForEach((_, v) => codeBuilder.AppendLine(v.GetCode()));
 124263            codeBuilder.AppendLine("}");
 124264            return codeBuilder.ToString();
 124265        }
 266
 267        public void SetInstanceValue<T>(string instanceName, string fieldName, T fieldValue)
 12268        {
 12269            SetInstanceValue(instanceName, fieldName, fieldValue, DateTime.UtcNow, QualityLevel.Good);
 12270        }
 271
 272        public void SetInstanceValue<T>(string instanceName, string fieldName, T fieldValue, DateTime timestamp)
 29273        {
 29274            SetInstanceValue(instanceName, fieldName, fieldValue, timestamp, QualityLevel.Good);
 29275        }
 276
 277        public void SetInstanceValue<T>(string instanceName, string fieldName, T fieldValue, QualityLevel qualityLevel)
 0278        {
 0279            SetInstanceValue(instanceName, fieldName, fieldValue, DateTime.UtcNow, qualityLevel);
 0280        }
 281
 282        public void SetInstanceValue<T>(string instanceName, string fieldName, T fieldValue, DateTime timestamp,
 283            QualityLevel qualityLevel)
 41284        {
 41285            Instance instance = GetInstance(instanceName);
 41286            IField field = instance.GetField(fieldName);
 41287            if (field is not Field<T> typedField)
 0288            {
 0289                throw new WrongFieldTypeException(fieldName);
 290            }
 291
 41292            typedField.SetValue(fieldValue, timestamp, qualityLevel);
 41293        }
 294
 295        public List<HistoryRow> GetHistoryValues(string instanceName, HistoryTimeRange query, List<string> fieldNames)
 3296        {
 3297            Instance instance = GetInstance(instanceName);
 3298            GetHistoryParametersForFields(instance, fieldNames,
 3299                out List<IFieldDefinition> fields, out HistoryRepository? historyRepository, out string? classTimeSerieI
 300
 3301            return GetHistoryValues(instanceName, query, fields, historyRepository!, classTimeSerieId!);
 2302        }
 303
 304        public List<HistoryRow> GetHistoryValues(string instanceName, HistoryTimeRange query, List<IFieldDefinition> fie
 305            HistoryRepository historyRepository, string classTimeSerieId)
 9306        {
 9307            ValidateTimeRange(query);
 8308            return HistoryStorageEngine!.GetHistoryValues(historyRepository!.HistoryStorageId!, classTimeSerieId!,
 8309                instanceName, query, fields);
 8310        }
 311
 312        public List<HistoryStatisticRow> GetHistoryStatistics(string instanceName, HistoryStatisticTimeRange query, List
 3313        {
 3314            Instance instance = GetInstance(instanceName);
 7315            GetHistoryParametersForFields(instance, [.. fieldNames.Select(field => field.Name)],
 3316                out List<IFieldDefinition> fields, out HistoryRepository? historyRepository, out string? classTimeSerieI
 3317            List<HistoryStatisticField> statFields = [];
 14318            for (int index = 0; index < fieldNames.Count; index++)
 4319            {
 4320                statFields.Add(new(fields[index], fieldNames[index].StatisticFunction));
 4321            }
 3322            return GetHistoryStatistics(instanceName, query, statFields, historyRepository!, classTimeSerieId!);
 1323        }
 324
 325        public List<HistoryStatisticRow> GetHistoryStatistics(string instanceName, HistoryStatisticTimeRange query, List
 326            HistoryRepository historyRepository, string classTimeSerieId)
 7327        {
 328            // if query range is a multiple of interval, remove 1 microsecond to end time (To) to avoid returning a new 
 7329            if ((query.To - query.From).Ticks % query.Interval.Ticks == 0)
 4330            {
 4331                query = query with { To = query.To.AddMicroseconds(-1) };
 4332            }
 7333            ValidateTimeRange(query);
 5334            return HistoryStorageEngine!.GetHistoryStatistics(historyRepository!.HistoryStorageId!, classTimeSerieId!,
 5335                instanceName, query, fields);
 5336        }
 337
 338        private static void ValidateTimeRange(HistoryTimeRange timeRange)
 16339        {
 16340            if (timeRange.From >= timeRange.To)
 2341            {
 2342                throw new BadHistoryStartTimeException(timeRange.From, timeRange.To);
 343            }
 14344        }
 345
 346        private static void ValidateTimeRange(HistoryStatisticTimeRange timeRange)
 7347        {
 7348            ValidateTimeRange(timeRange as HistoryTimeRange);
 6349            if (timeRange.Interval.Add(TimeSpan.FromMinutes(-1)) > timeRange.To - timeRange.From)
 1350            {
 1351                throw new BadHistoryIntervalException(timeRange.Interval, timeRange.From, timeRange.To);
 352            }
 5353        }
 354
 355        public static void GetHistoryParametersForFields(Instance instance, List<string> fieldNames,
 356            out List<IFieldDefinition> fields, out HistoryRepository? historyRepository, out string? classTimeSerieId)
 16357        {
 16358            fields = [];
 16359            historyRepository = null;
 16360            classTimeSerieId = null;
 184361            foreach (string fieldName in fieldNames)
 68362            {
 68363                IFieldDefinition field = instance.Class.GetField(fieldName);
 68364                IHistorizationProcessing? hp = field.ValuePostChangeProcessings
 68365                    .OfType<IHistorizationProcessing>()
 68366                    .FirstOrDefault();
 68367                if (hp != null)
 17368                {
 17369                    historyRepository = hp.HistoryRepository;
 17370                    classTimeSerieId = hp.ClassTimeSerieId;
 17371                }
 68372                fields.Add(field);
 68373            }
 16374            if (historyRepository is null || classTimeSerieId is null)
 0375            {
 0376                throw new NoHistoryStorageEngineException();
 377            }
 16378        }
 379    }
 380}

Methods/Properties

get_ProjectAssemblyLoaderWeakRef()
get_Instances()
.ctor()
.ctor(pva.SuperV.Engine.WipProject)
GetId()
SetupProjectAssemblyLoader()
CreateHistoryClassTimeSeries()
CreateHistoryRepositories(pva.SuperV.Engine.HistoryStorage.IHistoryStorageEngine)
CreateInstance(System.String,System.String,System.Boolean)
CreateInstance(System.Type)
GetConstructor(System.Type)
RemoveInstance(System.String)
GetInstance(System.String)
RecreateInstances(pva.SuperV.Engine.WipProject)
Unload()
GetCode()
SetInstanceValue(System.String,System.String,T)
SetInstanceValue(System.String,System.String,T,System.DateTime)
SetInstanceValue(System.String,System.String,T,pva.SuperV.Engine.QualityLevel)
SetInstanceValue(System.String,System.String,T,System.DateTime,pva.SuperV.Engine.QualityLevel)
GetHistoryValues(System.String,pva.SuperV.Engine.HistoryRetrieval.HistoryTimeRange,System.Collections.Generic.List`1<System.String>)
GetHistoryValues(System.String,pva.SuperV.Engine.HistoryRetrieval.HistoryTimeRange,System.Collections.Generic.List`1<pva.SuperV.Engine.IFieldDefinition>,pva.SuperV.Engine.HistoryStorage.HistoryRepository,System.String)
GetHistoryStatistics(System.String,pva.SuperV.Engine.HistoryRetrieval.HistoryStatisticTimeRange,System.Collections.Generic.List`1<pva.SuperV.Engine.HistoryRetrieval.HistoryStatisticFieldName>)
GetHistoryStatistics(System.String,pva.SuperV.Engine.HistoryRetrieval.HistoryStatisticTimeRange,System.Collections.Generic.List`1<pva.SuperV.Engine.HistoryRetrieval.HistoryStatisticField>,pva.SuperV.Engine.HistoryStorage.HistoryRepository,System.String)
ValidateTimeRange(pva.SuperV.Engine.HistoryRetrieval.HistoryTimeRange)
ValidateTimeRange(pva.SuperV.Engine.HistoryRetrieval.HistoryStatisticTimeRange)
GetHistoryParametersForFields(pva.SuperV.Engine.Instance,System.Collections.Generic.List`1<System.String>,System.Collections.Generic.List`1<pva.SuperV.Engine.IFieldDefinition>&,pva.SuperV.Engine.HistoryStorage.HistoryRepository&,System.String&)