diff --git a/src/TcoData/README.md b/src/TcoData/README.md new file mode 100644 index 000000000..d16bf479e --- /dev/null +++ b/src/TcoData/README.md @@ -0,0 +1,129 @@ +# TcoData +## Introduction + +The TcoData library is a comprehensive software framework designed to facilitate efficient data handling by remote task execution in industrial automation and control systems. This library serves as a bridge between programmable logic controllers (PLCs) and PC-based data repositories, enabling seamless communication, data manipulation, and task execution across distributed systems. + + +## Example of definition repository + +```csharp + var parameters = new MongoDbRepositorySettings<PlainSandboxData>("mongodb://localhost:27017", "TestDataBase", "TestCollection"); + var repository = Repository.Factory<PlainSandboxData>(parameters); + + // initialize repository + Entry.TcoDataTests.MAIN.sandbox.DataManager.InitializeRepository(repository); + // if data exchange PLC<->PC based system is reqired + Entry.TcoDataTests.MAIN.sandbox.DataManager.InitializeRemoteDataExchange(); +``` + + +## Example of fragmented repositories + +### PLC1 +```csharp +TYPE stProcessData_Plc1 EXTENDS TcoData.TcoEntity : +STRUCT + {attribute wpf [Container(Layout.Stack)]} + {attribute addProperty Name "Entity header"} + EntityHeader, stEntityHeader: BOOL; + _Modified : DT; + _Created : DT; + {attribute wpf [Container(Layout.Stack)]} + {attribute addProperty Name "Cu1"} + Cu_1 : stCu_ProcessData; +END_STRUCT +END_TYPE +``` + +```csharp +//<DataManagerDeclarations> +// Function block for data maipulation must extend from TcoData.TcoDataExchange. +FUNCTION_BLOCK TcoDataManagerPlc1 EXTENDS TcoData.TcoDataExchange +VAR + // This is the structure that contains the actual data we will work with. The `STRUCT` must extend `TcoData.TcoEntity` + _data : stProcessData_Plc1; +END_VAR +//</DataManagerDeclarations> +``` + +```csharp + var parametersFragmentedPlc1 = new MongoDbRepositorySettings<PlainstProcessData_Plc1>("mongodb://localhost:27017", "TestDataBase", "TestProcessData"); + + List<Expression<Func<PlainstProcessData_Plc1, PlainstProcessData_Plc1>>> fragmentExpressionPlc1 = new List<Expression<Func<PlainstProcessData_Plc1, PlainstProcessData_Plc1>>>(); + // here you can add members you interested in (option 1) + fragmentExpressionPlc1.Add(data => new PlainstProcessData_Plc1 { EntityHeader = data.EntityHeader }); + fragmentExpressionPlc1.Add(data => new PlainstProcessData_Plc1 { Cu_1 = data.Cu_1 }); + + var repositoryFragmentedPlc1 = new MongoDbFragmentedRepository<PlainstProcessData_Plc1, PlainstProcessData_Plc1>(parametersFragmentedPlc1, fragmentExpressionPlc1); + Entry.TcoDataTests.MAIN.sandbox.DataManagerPlc1.InitializeRepository(repositoryFragmentedPlc1); +``` +--- +**_Note:_** + +`DataManagerPlc1` will handle only with data defined in fragmentExpresion. That means only data defined in fragments for this repository will be updated.Data such as `_Modified` and `_Created` are skiped for writing (are not in list). + + #### Data created via UI view PLC 1 repository + + + + + #### Data storesd in repository PLC1 + + + +### PLC2 + +```csharp +TYPE stProcessData_Plc2 EXTENDS TcoData.TcoEntity : +STRUCT + {attribute wpf [Container(Layout.Stack)]} + {attribute addProperty Name "Entity header"} + EntityHeader, stEntityHeader: BOOL; + _Modified : DT; + _Created : DT; + {attribute wpf [Container(Layout.Stack)]} + {attribute addProperty Name "Cu2"} + Cu_2 : stCu_ProcessData; + +END_STRUCT +END_TYPE +``` +```csharp +//<DataManagerDeclarations> +// Function block for data maipulation must extend from TcoData.TcoDataExchange. +FUNCTION_BLOCK TcoDataManagerPlc2 EXTENDS TcoData.TcoDataExchange +VAR + // This is the structure that contains the actual data we will work with. The `STRUCT` must extend `TcoData.TcoEntity` + _data : stProcessData_Plc2; +END_VAR +//</DataManagerDeclarations> +``` + + +```csharp + var parametersFragmentedPlc2 = new MongoDbRepositorySettings<PlainstProcessData_Plc2>("mongodb://localhost:27017", "TestDataBase", "TestProcessData"); + + List<Expression<Func<PlainstProcessData_Plc2, PlainstProcessData_Plc2>>> fragmentExpressionPlc2 = new List<Expression<Func<PlainstProcessData_Plc2, PlainstProcessData_Plc2>>>(); + // here you can add members you interested in (option 2) + + fragmentExpressionPlc2.Add(data => new PlainstProcessData_Plc2 { EntityHeader = data.EntityHeader, Cu_2 = data.Cu_2 }); + ; + var repositoryFragmentedPlc2 = new MongoDbFragmentedRepository<PlainstProcessData_Plc2, PlainstProcessData_Plc2>(parametersFragmentedPlc2, fragmentExpressionPlc2); + Entry.TcoDataTests.MAIN.sandbox.DataManagerPlc2.InitializeRepository(repositoryFragmentedPlc2); +``` + +--- +**_Note:_** + +`DataManagerPlc2` will handle only with data defined in fragmentExpresion. That means only data defined in fragments for this repository will be updated.Data such as `_Modified` and `_Created` are skiped during writing. + + + + #### Data created via UI view PLC 2 repository + + + + + #### Data stored in repository PLC2 + + \ No newline at end of file diff --git a/src/TcoData/assets/fragmentedMongoPlc1.png b/src/TcoData/assets/fragmentedMongoPlc1.png new file mode 100644 index 000000000..1b87dbc8f Binary files /dev/null and b/src/TcoData/assets/fragmentedMongoPlc1.png differ diff --git a/src/TcoData/assets/fragmentedMongoPlc2.png b/src/TcoData/assets/fragmentedMongoPlc2.png new file mode 100644 index 000000000..5b74c8b37 Binary files /dev/null and b/src/TcoData/assets/fragmentedMongoPlc2.png differ diff --git a/src/TcoData/assets/fragmentedPlc1.png b/src/TcoData/assets/fragmentedPlc1.png new file mode 100644 index 000000000..3bc467171 Binary files /dev/null and b/src/TcoData/assets/fragmentedPlc1.png differ diff --git a/src/TcoData/assets/fragmentedPlc2.png b/src/TcoData/assets/fragmentedPlc2.png new file mode 100644 index 000000000..7cbb3e120 Binary files /dev/null and b/src/TcoData/assets/fragmentedPlc2.png differ diff --git a/src/TcoData/src/Repository/MongoDb/Mongo/MongoDbFragmentedRepository.cs b/src/TcoData/src/Repository/MongoDb/Mongo/MongoDbFragmentedRepository.cs new file mode 100644 index 000000000..c4d2565bc --- /dev/null +++ b/src/TcoData/src/Repository/MongoDb/Mongo/MongoDbFragmentedRepository.cs @@ -0,0 +1,364 @@ +using MongoDB.Bson; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using TcOpen.Inxton.Data; + +namespace TcOpen.Inxton.Data.MongoDb +{ + /// <summary> + /// Provides access to basic operations for MongoDB. + /// To use this code, mongo database must run somewhere. To start MongoDB locally you can use following code + /// + /// Start MongoDB without authentication + /// <code> + /// "C:\Program Files\MongoDB\Server\4.4\bin\mongod.exe" --dbpath C:\DATA\DB446\ + /// </code> + /// + /// Start MongoDB with authentication. You don't have to use the "--port" attribute or use a different "--dbpath". The only + /// reason why would you want to run authenticated database on a different dbpath and port simultaneously is if they're running + /// on the same machine. + /// + /// More info about the use credentials <see cref="MongoDbCredentials"/> + /// + /// <code> + /// "C:\Program Files\MongoDB\Server\4.4\bin\mongod.exe" --dbpath C:\DATA\DB446_AUTH\ --auth --port 27018 + /// </code> + /// </summary> + /// <typeparam name="T"></typeparam> + public class MongoDbFragmentedRepository<T,TFragment> : RepositoryBase<T> + where T : IBrowsableDataObject + { + private IMongoCollection<T> collection; + private List<Expression<Func<T, TFragment>>> _fragmentsExpresion; + List<(Expression<Func<T, TFragment>> FragmentExpression, TFragment FragmentData)> _updateFragments; + private readonly string location; + /// <summary> + /// Creates new instance of <see cref="MongoDbRepository{T}"/>. + /// </summary> + /// <param name="parameters">Repository settings</param> + /// <param name="updateFragments">Fragments and data</param> + public MongoDbFragmentedRepository(MongoDbRepositorySettings<T> parameters, List<(Expression<Func<T, TFragment>> FragmentExpression, TFragment FragmentData)> updateFragments) + { + location = parameters.GetConnectionInfo(); + this.collection = parameters.Collection; + _updateFragments = updateFragments; + + } + /// <summary> + /// Creates new instance of <see cref="MongoDbRepository{T}"/>. + /// </summary> + /// <param name="parameters">Repository settings</param> + /// <param name="fragmentsExpression">Expressions fragments witch will be added into data exchange </param> + public MongoDbFragmentedRepository(MongoDbRepositorySettings<T> parameters,List<Expression<Func<T, TFragment>>> fragmentsExpression) + { + location = parameters.GetConnectionInfo(); + this.collection = parameters.Collection; + _fragmentsExpresion = fragmentsExpression; + + } + + private bool RecordExists(string identifier) + { return collection.Find(p => p._EntityId == identifier).Count() >= 1; } + + protected override void CreateNvi(string identifier, T data) + { + try + { + var x = System.Threading.Thread.CurrentThread.GetApartmentState(); + + if (RecordExists(identifier)) + { + throw new DuplicateIdException($"Record with ID {identifier} already exists in this collection.", + null); + } + + data._recordId = ObjectId.GenerateNewId(); + + collection.InsertOne(data); + + } + catch (Exception ex) + { + throw ex; + } + } + + protected override void DeleteNvi(string identifier) { collection.DeleteOne(p => p._EntityId == identifier); } + + protected override long FilteredCountNvi(string id, eSearchMode searchMode) + { + var filetered = new List<T>(); + FilterDefinition<T> filter; + + var filterExpresion = ParseIdentifierForRegularExpression(id); + + switch (searchMode) + { + case eSearchMode.StartsWith: + filter = Builders<T>.Filter.Regex(p => p._EntityId, new BsonRegularExpression($"^{filterExpresion}", "")); + break; + case eSearchMode.Contains: + filter = Builders<T>.Filter.Regex(p => p._EntityId, new BsonRegularExpression($".*{filterExpresion}", "")); + break; + case eSearchMode.Exact: + default: + filter = Builders<T>.Filter.Eq(p => p._EntityId, id); + break; + } + + if (id == "*" || string.IsNullOrWhiteSpace(id)) + { +#pragma warning disable CS0618 // CountDocuments is very slow compared to Count() even though Count is obsolete. + return collection + .Find(new BsonDocument()) + .Count(); + } + else + { + return collection + .Find(filter) + .Count(); + } + } +#pragma warning restore CS0618 + + + protected override IEnumerable<T> GetRecordsNvi(string identifier, int limit, int skip, eSearchMode searchMode) + { + var filetered = new List<T>(); + FilterDefinition<T> filter; + + var filterExpresion = ParseIdentifierForRegularExpression(identifier); + + switch (searchMode) + { + case eSearchMode.StartsWith: + filter = Builders<T>.Filter.Regex(p => p._EntityId, new BsonRegularExpression($"^{filterExpresion}", "")); + break; + case eSearchMode.Contains: + filter = Builders<T>.Filter.Regex(p => p._EntityId, new BsonRegularExpression($".*{filterExpresion}", "")); + break; + case eSearchMode.Exact: + default: + filter = Builders<T>.Filter.Eq(p => p._EntityId, identifier); + break; + } + + + if (identifier == "*" || string.IsNullOrWhiteSpace(identifier)) + { + return collection + .Find(new BsonDocument()) + .Sort(new SortDefinitionBuilder<T>().Descending("$natural")) + .Limit(limit) + .Skip(skip) + .ToList(); + } + else + { + return collection + .Find(filter) + .Limit(limit) + .Skip(skip) + .ToList(); + } + } + + /// <summary> + /// Parses input string, so it is evaluated as verbatim string and not as regular expression. All special ascii characters are prefixed with "\". + /// </summary> + /// <param name="identifier"></param> + /// <returns></returns> + private string ParseIdentifierForRegularExpression(string identifier) + { + var result = string.Empty; + + if (string.IsNullOrWhiteSpace(identifier)) + { + return result; + } + + foreach (var character in identifier) + { + if (character <= 47 || (character >= 58 && character <= 64) || (character >= 91 && character <= 96) || (character >= 123 && character <= 126)) + { + result += @"\"; + } + result += character.ToString(); + } + + return result; + } + + protected override T ReadNvi(string identifier) + { + try + { + var record = collection.Find(p => p._EntityId == identifier).FirstOrDefault(); + if (record == null) + { + throw new UnableToLocateRecordId($"Unable to locate record with ID: {identifier} in {location}.", + null); + } + + return record; + } + catch (Exception ex) + { + throw ex; + } + } + + protected override void UpdateNvi(string identifier, T data) + { + if (_fragmentsExpresion != null) + UpdateNvi(identifier, _fragmentsExpresion, data); + else if (_updateFragments != null) + UpdateNvi(identifier, _updateFragments); + else + Replace(identifier, data); + + + } + + + protected void UpdateNvi(string identifier, List<Expression<Func<T, TFragment>>> fragmentsExpression, T data ) + { + try + { + if (!RecordExists(identifier)) + { + throw new UnableToLocateRecordId($"Unable to locate record with ID: {identifier} in {location}.", + null); + } + + var filter = Builders<T>.Filter.Eq("_EntityId", identifier); + + + collection.UpdateOne(filter, ConvertExpressionsToUpdateDefinition(fragmentsExpression,data)); + + } + catch (Exception ex) + { + throw new UnableToUpdateRecord($"Unable to update record ID:{identifier} in {location}.", ex); + } + } + + static UpdateDefinition<T> ConvertExpressionsToUpdateDefinition(List<Expression<Func<T, TFragment>>> expressions ,T data) + { + var updateBuilder = Builders<T>.Update; + var updateDefinitions = new List<UpdateDefinition<T>>(); + + foreach (var expression in expressions) + { + var body = expression.Body as MemberInitExpression; + + if (body == null) + { + throw new ArgumentException("Invalid expression format. Expected MemberInitExpression."); + } + + foreach (var binding in body.Bindings) + { + var memberAssignment = binding as MemberAssignment; + + if (memberAssignment != null) + { + var propertyName = GetPropertyName(memberAssignment.Member); + Type objectType = typeof(T); + PropertyInfo propertyInfo = objectType.GetProperty(propertyName); + var propertyValue = propertyInfo.GetValue(data); + var updateDefinition = updateBuilder.Set(propertyName, propertyValue); + updateDefinitions.Add(updateDefinition); + } + } + } + + return updateBuilder.Combine(updateDefinitions); + } + + // Helper method to get the name of a property from a member expression + static string GetPropertyName(MemberInfo member) + { + var propertyInfo = member as PropertyInfo; + + if (propertyInfo == null) + { + throw new ArgumentException("Member is not a PropertyInfo."); + } + + return propertyInfo.Name; + } + + // Helper method to evaluate expression and retrieve property value + static object GetValueFromExpression(Expression expression) + { + var lambda = Expression.Lambda(expression).Compile(); + return lambda.DynamicInvoke(); + } + protected void UpdateNvi(string identifier, List<(Expression<Func<T, TFragment>> FragmentExpression, TFragment FragmentData)> fragments) + { + try + { + if (!RecordExists(identifier)) + { + throw new UnableToLocateRecordId($"Unable to locate record with ID: {identifier} in {location}.", + null); + } + + var filter = Builders<T>.Filter.Eq("_EntityId", identifier); + var updateDefinitions = new List<UpdateDefinition<T>>(); + + + foreach (var item in fragments) + { + updateDefinitions.Add(Builders<T>.Update.Set(item.FragmentExpression, item.FragmentData)); + } + + + collection.UpdateOne(filter, Builders<T>.Update.Combine(updateDefinitions)); + + } + catch (Exception ex) + { + throw new UnableToUpdateRecord($"Unable to update record ID:{identifier} in {location}.", ex); + } + } + + protected void Replace(string identifier, T data) + { + try + { + if (!RecordExists(identifier)) + { + throw new UnableToLocateRecordId($"Unable to locate record with ID: {identifier} in {location}.", + null); + } + + collection.ReplaceOne(p => p._EntityId == identifier, data); + + } + catch (Exception ex) + { + throw new UnableToUpdateRecord($"Unable to update record ID:{identifier} in {location}.", ex); + } + } + protected override bool ExistsNvi(string identifier) + { + return RecordExists(identifier); + } + + protected override long CountNvi => collection.Count(new BsonDocument()); + + /// <summary> + /// Gets the <see cref="IMongoCollection{T}"/> of this repository. + /// </summary> + public IMongoCollection<T> Collection => this.collection; + + public override IQueryable<T> Queryable => this.collection.AsQueryable(); + } +} diff --git a/src/TcoData/src/XAE/XAE/TcoData/POUs/Data/TcoDataTask.TcPOU b/src/TcoData/src/XAE/XAE/TcoData/POUs/Data/TcoDataTask.TcPOU index a54b6bdad..fcbc171d1 100644 --- a/src/TcoData/src/XAE/XAE/TcoData/POUs/Data/TcoDataTask.TcPOU +++ b/src/TcoData/src/XAE/XAE/TcoData/POUs/Data/TcoDataTask.TcPOU @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.9"> - <POU Name="TcoDataTask" Id="{7211395c-b0ba-0bf9-25be-0541f79b5876}" SpecialFunc="None"> +<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.12"> + <POU Name="TcoDataTask" Id="{d746b7c0-aeef-0bb8-363a-a8b912d682b6}" SpecialFunc="None"> <Declaration><![CDATA[(*~ <docu> <summary> @@ -36,7 +36,7 @@ END_VAR <ST><![CDATA[]]></ST> </Implementation> </Method> - <Method Name="WithId" Id="{73957c41-6f21-008e-2bf1-1954b1beab89}"> + <Method Name="WithId" Id="{d8ebbe97-9c86-0568-17c4-bcc05d378bb1}"> <Declaration><![CDATA[(*~ <docu> <summary> diff --git a/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Data/_dummyData/stProcessData.TcDUT b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Data/_dummyData/stProcessData.TcDUT index aaccbf785..838451f5e 100644 --- a/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Data/_dummyData/stProcessData.TcDUT +++ b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Data/_dummyData/stProcessData.TcDUT @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> -<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.9"> +<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.12"> <DUT Name="stProcessData" Id="{c526ee09-51fa-4750-a7bd-5ca62f4bed32}"> <Declaration><![CDATA[{attribute wpf [Container(Layout.Tabs)]} TYPE stProcessData EXTENDS TcoData.TcoEntity : STRUCT {attribute wpf [Container(Layout.Stack)]} {attribute addProperty Name "Entity header"} - EntityHeader, stEntityHeader: BOOL; + EntityHeader , stEntityHeader: BOOL; _Modified : DT; _Created : DT; {attribute wpf [Container(Layout.Stack)]} diff --git a/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/SandboxContext.TcPOU b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/SandboxContext.TcPOU index 72a372e7f..67dadc1aa 100644 --- a/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/SandboxContext.TcPOU +++ b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/SandboxContext.TcPOU @@ -1,11 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> -<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.10"> +<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.12"> <POU Name="SandboxContext" Id="{ed8218eb-5f5f-0973-2882-9b75bfb2ad4d}" SpecialFunc="None"> <Declaration><![CDATA[ //<Declaration> FUNCTION_BLOCK SandboxContext EXTENDS TcoCore.TcoContext VAR DataManager : TcoDataManager(THIS^); + + DataManagerFragmented : TcoDataManager(THIS^); + + DataManagerPlc1 : TcoDataManagerPlc1(THIS^); + + DataManagerPlc2 : TcoDataManagerPlc2(THIS^); + +// DataManagerFragmentedPlc1 : TcoDataManagerPlc1(THIS^); END_VAR //</Declaration> VAR @@ -38,9 +46,14 @@ Context.Environment.Messaging.MessengerLoggingMethod := eMessengerLogMethod.OnEv //<CyclicalDataMangerCall> // Cyclical data manage call. DataManager(); +DataManagerFragmented(); +DataManagerPlc1(); +DataManagerPlc2(); + //</CyclicalDataMangerCall> + recordId_1 := 'scoundrel'; recordId_2 := 'knight'; diff --git a/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/SandboxData_Plc1.TcDUT b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/SandboxData_Plc1.TcDUT new file mode 100644 index 000000000..d88a982aa --- /dev/null +++ b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/SandboxData_Plc1.TcDUT @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.12"> + <DUT Name="SandboxData_Plc1" Id="{627634c0-8b5d-0052-09c9-8056ed5f97db}"> + <Declaration><![CDATA[//<SandboxData> + +// DATA STRUCTURE ELIGIBLE FOR USE WITH TcoDataExchange. +TYPE + SandboxData_Plc1 EXTENDS TcoData.TcoEntity : + STRUCT + sampleData : SampleDataStructure; + someIntegerPlc1 : INT; + someStringPlc1 : STRING; + END_STRUCT +END_TYPE +//</SandboxData> +]]></Declaration> + </DUT> +</TcPlcObject> \ No newline at end of file diff --git a/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/TcoDataManagerPlc1.TcPOU b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/TcoDataManagerPlc1.TcPOU new file mode 100644 index 000000000..ef3ee1062 --- /dev/null +++ b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/TcoDataManagerPlc1.TcPOU @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.12"> + <POU Name="TcoDataManagerPlc1" Id="{9a7c20f5-5ec4-0a8b-1523-1e641a2c3703}" SpecialFunc="None"> + <Declaration><![CDATA[//<DataManagerDeclarations> +// Function block for data maipulation must extend from TcoData.TcoDataExchange. +FUNCTION_BLOCK TcoDataManagerPlc1 EXTENDS TcoData.TcoDataExchange +VAR + // This is the structure that contains the actual data we will work with. The `STRUCT` must extend `TcoData.TcoEntity` + _data : stProcessData_Plc1; +END_VAR +//</DataManagerDeclarations> +]]></Declaration> + <Implementation> + <ST><![CDATA[//---------------------------------- +//<DataManagerSuperCall> + +// IMPLEMENTATION BODY OF FUNCTION BLOCK + +// IMPORTANT: SUPER CALL HERE IS REQUIRED TO CALL THE DATA MANAGER TASKS!!! +SUPER^(); + +//</DataManagerSuperCall> +//---------------------------------- + + +]]></ST> + </Implementation> + <Property Name="Data" Id="{76076a73-aed0-0ed6-249c-f33af109b0e1}"> + <Declaration><![CDATA[PROPERTY Data : REFERENCE TO stProcessData_Plc1]]></Declaration> + <Get Name="Get" Id="{4fb95632-6280-0a67-1f72-4c17a2bf2032}"> + <Declaration><![CDATA[VAR +END_VAR +]]></Declaration> + <Implementation> + <ST><![CDATA[Data REF= _data;]]></ST> + </Implementation> + </Get> + </Property> + </POU> +</TcPlcObject> \ No newline at end of file diff --git a/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/TcoDataManagerPlc2.TcPOU b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/TcoDataManagerPlc2.TcPOU new file mode 100644 index 000000000..b4a82b71d --- /dev/null +++ b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/TcoDataManagerPlc2.TcPOU @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.12"> + <POU Name="TcoDataManagerPlc2" Id="{7f2c29b4-5945-0f8a-0265-483f7728ce61}" SpecialFunc="None"> + <Declaration><![CDATA[//<DataManagerDeclarations> +// Function block for data maipulation must extend from TcoData.TcoDataExchange. +FUNCTION_BLOCK TcoDataManagerPlc2 EXTENDS TcoData.TcoDataExchange +VAR + // This is the structure that contains the actual data we will work with. The `STRUCT` must extend `TcoData.TcoEntity` + _data : stProcessData_Plc2; +END_VAR +//</DataManagerDeclarations> +]]></Declaration> + <Implementation> + <ST><![CDATA[//---------------------------------- +//<DataManagerSuperCall> + +// IMPLEMENTATION BODY OF FUNCTION BLOCK + +// IMPORTANT: SUPER CALL HERE IS REQUIRED TO CALL THE DATA MANAGER TASKS!!! +SUPER^(); + +//</DataManagerSuperCall> +//---------------------------------- + + +]]></ST> + </Implementation> + <Property Name="Data" Id="{4507aec0-1a86-0024-1ed7-c00ff9cadd07}"> + <Declaration><![CDATA[PROPERTY Data : REFERENCE TO stProcessData_Plc2 +]]></Declaration> + <Get Name="Get" Id="{13afbc6d-e055-0b4f-0ce3-f97a5f92b723}"> + <Declaration><![CDATA[VAR +END_VAR +]]></Declaration> + <Implementation> + <ST><![CDATA[Data REF= _data;]]></ST> + </Implementation> + </Get> + </Property> + </POU> +</TcPlcObject> \ No newline at end of file diff --git a/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/stProcessData_Plc1.TcDUT b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/stProcessData_Plc1.TcDUT new file mode 100644 index 000000000..3584caf72 --- /dev/null +++ b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/stProcessData_Plc1.TcDUT @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.12"> + <DUT Name="stProcessData_Plc1" Id="{d24bdb96-3d44-0766-068c-d6dfe8ca0564}"> + <Declaration><![CDATA[{attribute wpf [Container(Layout.Tabs)]} +TYPE stProcessData_Plc1 EXTENDS TcoData.TcoEntity : +STRUCT + {attribute wpf [Container(Layout.Stack)]} + {attribute addProperty Name "Entity header"} + EntityHeader, stEntityHeader: BOOL; + _Modified : DT; + _Created : DT; + {attribute wpf [Container(Layout.Stack)]} + {attribute addProperty Name "Cu1"} + // {attribute addProperty Name "ST 1"} + Cu_1 : stCu_ProcessData; + (*Cu_2 : stCu_ProcessData; + Cu_3 : stCu_ProcessData; + Cu_4 : stCu_ProcessData; + Cu_5 : stCu_ProcessData; + Cu_6 : stCu_ProcessData; + Cu_7 : stCu_ProcessData; + Cu_8 : stCu_ProcessData; + Cu_9 : stCu_ProcessData; + Cu_10 : stCu_ProcessData; + *) +END_STRUCT +END_TYPE +]]></Declaration> + </DUT> +</TcPlcObject> \ No newline at end of file diff --git a/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/stProcessData_Plc2.TcDUT b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/stProcessData_Plc2.TcDUT new file mode 100644 index 000000000..ba91eaf76 --- /dev/null +++ b/src/TcoData/src/XAE/XAE/TcoDataTests/POUs/Examples/stProcessData_Plc2.TcDUT @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4024.12"> + <DUT Name="stProcessData_Plc2" Id="{dbbe4a07-986b-0fc8-0d6a-544726ed2b58}"> + <Declaration><![CDATA[{attribute wpf [Container(Layout.Tabs)]} +TYPE stProcessData_Plc2 EXTENDS TcoData.TcoEntity : +STRUCT + {attribute wpf [Container(Layout.Stack)]} + {attribute addProperty Name "Entity header"} + EntityHeader, stEntityHeader: BOOL; + _Modified : DT; + _Created : DT; + {attribute wpf [Container(Layout.Stack)]} + // {attribute addProperty Name "ST 1"} + //Cu_1 : stCu_ProcessData; + {attribute addProperty Name "Cu2"} + Cu_2 : stCu_ProcessData; + (*Cu_3 : stCu_ProcessData; + Cu_4 : stCu_ProcessData; + Cu_5 : stCu_ProcessData; + Cu_6 : stCu_ProcessData; + Cu_7 : stCu_ProcessData; + Cu_8 : stCu_ProcessData; + Cu_9 : stCu_ProcessData; + Cu_10 : stCu_ProcessData; + *) +END_STRUCT +END_TYPE +]]></Declaration> + </DUT> +</TcPlcObject> \ No newline at end of file diff --git a/src/TcoData/src/XAE/XAE/TcoDataTests/TcoDataTests.plcproj b/src/TcoData/src/XAE/XAE/TcoDataTests/TcoDataTests.plcproj index 62ef66056..d739a0318 100644 --- a/src/TcoData/src/XAE/XAE/TcoDataTests/TcoDataTests.plcproj +++ b/src/TcoData/src/XAE/XAE/TcoDataTests/TcoDataTests.plcproj @@ -69,6 +69,21 @@ <Compile Include="POUs\Examples\eExamplesStates.TcDUT"> <SubType>Code</SubType> </Compile> + <Compile Include="POUs\Examples\SandboxData_Plc1.TcDUT"> + <SubType>Code</SubType> + </Compile> + <Compile Include="POUs\Examples\stProcessData_Plc1.TcDUT"> + <SubType>Code</SubType> + </Compile> + <Compile Include="POUs\Examples\stProcessData_Plc2.TcDUT"> + <SubType>Code</SubType> + </Compile> + <Compile Include="POUs\Examples\TcoDataManagerPlc1.TcPOU"> + <SubType>Code</SubType> + </Compile> + <Compile Include="POUs\Examples\TcoDataManagerPlc2.TcPOU"> + <SubType>Code</SubType> + </Compile> <Compile Include="POUs\MAIN.TcPOU"> <SubType>Code</SubType> </Compile> @@ -109,7 +124,6 @@ </PlaceholderReference> <PlaceholderReference Include="TcoCore"> <DefaultResolution>TcoCore, 0.6.0.913 (Vortex.Library)</DefaultResolution> - <Namespace>TcoCore</Namespace> </PlaceholderReference> <PlaceholderReference Include="TcoData"> <DefaultResolution>TcoData, 0.6.0.913 (Vortex.Library)</DefaultResolution> diff --git a/src/TcoData/src/XAE/XAE/XAETcoData.tsproj b/src/TcoData/src/XAE/XAE/XAETcoData.tsproj index abebcc269..85d27df68 100644 --- a/src/TcoData/src/XAE/XAE/XAETcoData.tsproj +++ b/src/TcoData/src/XAE/XAE/XAETcoData.tsproj @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<TcSmProject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.beckhoff.com/schemas/2012/07/TcSmProject" TcSmVersion="1.0" TcVersion="3.1.4024.25"> +<TcSmProject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.beckhoff.com/schemas/2012/07/TcSmProject" TcSmVersion="1.0" TcVersion="3.1.4024.55"> <Project ProjectGUID="{F9E8CB0C-87A9-4377-9234-E2ACBFE1E5FE}" TargetNetId="192.168.4.1.1.1" Target64Bit="true" ShowHideConfigurations="#x106"> <System> <Tasks> @@ -10,12 +10,12 @@ </System> <Plc> <Project GUID="{C292F353-C8D8-4BBB-BE1E-9772558B17F8}" Name="TcoData" PrjFilePath="TcoData\TcoData.plcproj" TmcFilePath="TcoData\TcoData.tmc" ReloadTmc="true" AmsPort="851" FileArchiveSettings="#x000e" SymbolicMapping="true"> - <Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcPath="TcoData\TcoData.tmc"> + <Instance Id="#x08502000" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcPath="TcoData\TcoData.tmc" TmcHash="{0A95D8E1-D46E-B125-AB26-F27705306E8F}"> <Name>TcoData Instance</Name> <CLSID ClassFactory="TcPlc30">{08500001-0000-0000-F000-000000000064}</CLSID> <Contexts> <Context> - <Id NeedCalleeCall="true">0</Id> + <Id>0</Id> <Name>PlcTask</Name> <ManualConfig> <OTCID>#x02010030</OTCID> @@ -30,12 +30,12 @@ </Instance> </Project> <Project GUID="{6025FACE-83D2-4500-B4A4-6C4CD6227637}" Name="TcoDataTests" PrjFilePath="TcoDataTests\TcoDataTests.plcproj" TmcFilePath="TcoDataTests\TcoDataTests.tmc" ReloadTmc="true" AmsPort="852" FileArchiveSettings="#x000e" SymbolicMapping="true"> - <Instance Id="#x08502040" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcPath="TcoDataTests\TcoDataTests.tmc"> + <Instance Id="#x08502040" TcSmClass="TComPlcObjDef" KeepUnrestoredLinks="2" TmcPath="TcoDataTests\TcoDataTests.tmc" TmcHash="{D5DE3E16-563D-CF3A-9EDE-8F2F7DBA8FB5}"> <Name>TcoDataTests Instance</Name> <CLSID ClassFactory="TcPlc30">{08500001-0000-0000-F000-000000000064}</CLSID> <Contexts> <Context> - <Id NeedCalleeCall="true">0</Id> + <Id>0</Id> <Name>PlcTask</Name> <ManualConfig> <OTCID>#x02010030</OTCID> diff --git a/src/TcoData/tests/Sandbox.TcoData.Wpf/App.xaml.cs b/src/TcoData/tests/Sandbox.TcoData.Wpf/App.xaml.cs index 705dcc143..d95e1d98d 100644 --- a/src/TcoData/tests/Sandbox.TcoData.Wpf/App.xaml.cs +++ b/src/TcoData/tests/Sandbox.TcoData.Wpf/App.xaml.cs @@ -1,4 +1,7 @@ using Serilog; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; using System.Windows; using TcoDataTests; using TcOpen.Inxton.Data; @@ -13,6 +16,9 @@ public partial class App : Application { public App() { + + + //TcoCore.Threading.Dispatcher.SetDispatcher(TcoCore.Wpf.Threading.Dispatcher.Get); Entry.TcoDataTests.Connector.BuildAndStart(); @@ -42,6 +48,49 @@ public App() Entry.TcoDataTests.MAIN.sandbox.DataManager.InitializeRepository(repository); Entry.TcoDataTests.MAIN.sandbox.DataManager.InitializeRemoteDataExchange(); + + + + + + //var parametersFragmented = new MongoDbRepositorySettings<PlainSandboxData>("mongodb://localhost:27017", "TestDataBase", "TestCollectionFragmented"); + //var updateFragments = new List<(Expression<Func<PlainSandboxData, PlainSampleDataStructure>>, PlainSampleDataStructure)> + //{ + // { (doc=>doc.sampleData , new PlainSampleDataStructure(){ SampleString = "SI KRAL"} ) }, + + //}; + //; + //var repositoryFragmented = new MongoDbFragmentedRepository<PlainSandboxData, PlainSandboxData>(parametersFragmented, updateFragments); + + + + var parametersFragmented = new MongoDbRepositorySettings<PlainSandboxData>("mongodb://localhost:27017", "TestDataBase", "TestCollectionFragmented"); + List<Expression<Func<PlainSandboxData, PlainSandboxData>>> fragmentExpression = new List<Expression<Func<PlainSandboxData, PlainSandboxData>>>(); + fragmentExpression.Add(data => new PlainSandboxData { someString = data.someString, someInteger = data.someInteger, sampleData =data.sampleData }); + ; + var repositoryFragmented = new MongoDbFragmentedRepository<PlainSandboxData, PlainSandboxData>(parametersFragmented, fragmentExpression); + + + Entry.TcoDataTests.MAIN.sandbox.DataManagerFragmented.InitializeRepository(repositoryFragmented); + + + var parametersFragmentedPlc1 = new MongoDbRepositorySettings<PlainstProcessData_Plc1>("mongodb://localhost:27017", "TestDataBase", "TestProcessData"); + + List<Expression<Func<PlainstProcessData_Plc1, PlainstProcessData_Plc1>>> fragmentExpressionPlc1 = new List<Expression<Func<PlainstProcessData_Plc1, PlainstProcessData_Plc1>>>(); + //fragmentExpressionPlc1.Add(data => new PlainstProcessData_Plc1 { EntityHeader = data.EntityHeader,/*_Modified = data._Modified */}); + fragmentExpressionPlc1.Add(data => new PlainstProcessData_Plc1 { Cu_1 = data.Cu_1 }); + + var repositoryFragmentedPlc1 = new MongoDbFragmentedRepository<PlainstProcessData_Plc1, PlainstProcessData_Plc1>(parametersFragmentedPlc1, fragmentExpressionPlc1); + Entry.TcoDataTests.MAIN.sandbox.DataManagerPlc1.InitializeRepository(repositoryFragmentedPlc1); + + + var parametersFragmentedPlc2 = new MongoDbRepositorySettings<PlainstProcessData_Plc2>("mongodb://localhost:27017", "TestDataBase", "TestProcessData"); + + List<Expression<Func<PlainstProcessData_Plc2, PlainstProcessData_Plc2>>> fragmentExpressionPlc2 = new List<Expression<Func<PlainstProcessData_Plc2, PlainstProcessData_Plc2>>>(); + fragmentExpressionPlc2.Add(data => new PlainstProcessData_Plc2 { EntityHeader = data.EntityHeader, Cu_2 = data.Cu_2 /*someInteger = data.someInteger*/ }); + ; + var repositoryFragmentedPlc2 = new MongoDbFragmentedRepository<PlainstProcessData_Plc2, PlainstProcessData_Plc2>(parametersFragmentedPlc2, fragmentExpressionPlc2); + Entry.TcoDataTests.MAIN.sandbox.DataManagerPlc2.InitializeRepository(repositoryFragmentedPlc2); } } } diff --git a/src/TcoData/tests/Sandbox.TcoData.Wpf/MainWindow.xaml b/src/TcoData/tests/Sandbox.TcoData.Wpf/MainWindow.xaml index d1bf1ab69..936fa5a08 100644 --- a/src/TcoData/tests/Sandbox.TcoData.Wpf/MainWindow.xaml +++ b/src/TcoData/tests/Sandbox.TcoData.Wpf/MainWindow.xaml @@ -43,6 +43,77 @@ <Button Grid.Row="2" Click="Button_Click">ExternalInvokeTest</Button> </Grid> + </TabItem> + <TabItem Header="DataFragmented"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + + </Grid.RowDefinitions> + <Grid Grid.Row="0"> + <vortex:RenderableContentControl DataContext="{Binding Plc.MAIN.sandbox.DataManagerFragmented}" PresentationType="Control"> + <vortex:RenderableContentControl.RootContainer> + <Grid /> + </vortex:RenderableContentControl.RootContainer> + </vortex:RenderableContentControl> + </Grid> + <vortex:TcoDataExchangeSimpleSelectorView Grid.Row="1" DataContext="{Binding Plc.MAIN.sandbox.DataManagerFragmented}"></vortex:TcoDataExchangeSimpleSelectorView> + <!--<UniformGrid + Grid.Row="1" + Columns="2" + Rows="0"> + <vortex:DynamicTreeView x:Name="dynamicTreeView" DataContext="{Binding Plc.MAIN.sandbox, Mode=OneWay}" /> + <ScrollViewer> + <vortex:ObservableContentControl DataContext="{Binding SelectedItem, ElementName=dynamicTreeView}" PresentationType="Base-Control" /> + </ScrollViewer> + </UniformGrid>--> + <!--<Button Grid.Row="2" Click="Button_Click">ExternalInvokeTest</Button>--> + </Grid> + + </TabItem> + + <TabItem Header="DataFragmented PLC1"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + + </Grid.RowDefinitions> + <Grid Grid.Row="0"> + <vortex:RenderableContentControl DataContext="{Binding Plc.MAIN.sandbox.DataManagerPlc1}" PresentationType="Control"> + <vortex:RenderableContentControl.RootContainer> + <Grid /> + </vortex:RenderableContentControl.RootContainer> + </vortex:RenderableContentControl> + </Grid> + + </Grid> + + </TabItem> + <TabItem Header="DataFragmented PLC2"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + + </Grid.RowDefinitions> + <Grid Grid.Row="0"> + <vortex:RenderableContentControl DataContext="{Binding Plc.MAIN.sandbox.DataManagerPlc2}" PresentationType="Control"> + <vortex:RenderableContentControl.RootContainer> + <Grid /> + </vortex:RenderableContentControl.RootContainer> + </vortex:RenderableContentControl> + </Grid> + + </Grid> + </TabItem> <TabItem Header="Diagnostics"> <vortex:RenderableContentControl DataContext="{Binding Plc.MAIN.sandbox}" PresentationType="Diagnostics" />