diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-12-01 07:40:45 +0200 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2020-12-01 07:40:45 +0200 |
| commit | 45a7a7319bbae1a2ab5cfc93d7a2507cccd8770e (patch) | |
| tree | 60a11ce93471cc99e30e180304d149727f3fba69 | |
| parent | 4225ba2cde1b0cfdb57196cb832dbec2dfca5707 (diff) | |
| download | Tango-45a7a7319bbae1a2ab5cfc93d7a2507cccd8770e.tar.gz Tango-45a7a7319bbae1a2ab5cfc93d7a2507cccd8770e.zip | |
Implemented action logs for data store.
11 files changed, 217 insertions, 56 deletions
diff --git a/Software/Visual_Studio/DataStore/Tango.DataStore.EF/EFDataStoreHelper.cs b/Software/Visual_Studio/DataStore/Tango.DataStore.EF/EFDataStoreHelper.cs index 5e885458b..a702c3465 100644 --- a/Software/Visual_Studio/DataStore/Tango.DataStore.EF/EFDataStoreHelper.cs +++ b/Software/Visual_Studio/DataStore/Tango.DataStore.EF/EFDataStoreHelper.cs @@ -12,55 +12,12 @@ namespace Tango.DataStore.EF { public static byte[] CreateBytes(DataType type, Object obj) { - switch (type) - { - case DataType.Int32: - return BitConverter.GetBytes((int)obj); - case DataType.Float: - return BitConverter.GetBytes((float)obj); - case DataType.Double: - return BitConverter.GetBytes((double)obj); - case DataType.Boolean: - return BitConverter.GetBytes((bool)obj); - case DataType.String: - return Encoding.Default.GetBytes(obj.ToString()); - case DataType.Bytes: - return (byte[])obj; - case DataType.Proto: - if (obj is DataStoreProtoObject protoMessage) - { - return protoMessage.ToBytes(); - } - else - { - throw new NotSupportedException($"Data type is 'Proto' but object is not of type '{nameof(DataStoreProtoObject)}'."); - } - } - - throw new NotSupportedException("The specified type is not supported."); + return DataStoreHelper.CreateBytes(type, obj); } public static Object CreateObject(DataType type, byte[] bytes) { - switch (type) - { - case DataType.Int32: - return BitConverter.ToInt32(bytes, 0); - case DataType.Float: - return BitConverter.ToSingle(bytes, 0); - case DataType.Double: - return BitConverter.ToDouble(bytes, 0); - case DataType.Boolean: - return BitConverter.ToBoolean(bytes, 0); - case DataType.String: - return Encoding.Default.GetString(bytes); - case DataType.Bytes: - return bytes; - case DataType.Proto: - return DataStoreProtoObject.FromBytes(bytes); - } - - throw new NotSupportedException("The specified type is not supported."); + return DataStoreHelper.CreateObject(type, bytes); } public static IDataStoreItem CreateDataStoreItem(DataStoreItem item) diff --git a/Software/Visual_Studio/DataStore/Tango.DataStore/DataStoreHelper.cs b/Software/Visual_Studio/DataStore/Tango.DataStore/DataStoreHelper.cs index 0ceecd81b..0409dbf7a 100644 --- a/Software/Visual_Studio/DataStore/Tango.DataStore/DataStoreHelper.cs +++ b/Software/Visual_Studio/DataStore/Tango.DataStore/DataStoreHelper.cs @@ -83,17 +83,28 @@ namespace Tango.DataStore /// <returns></returns> public static String FormatDataStoreItem(IDataStoreItem item) { - if (item.Type == DataType.Bytes) + return FormatDataStoreValue(item.Type, item.Value); + } + + /// <summary> + /// Formats the data store value. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="obj">The object.</param> + /// <returns></returns> + public static String FormatDataStoreValue(DataType type, object obj) + { + if (type == DataType.Bytes) { - return Convert.ToBase64String((byte[])item.Value); + return Convert.ToBase64String((byte[])obj); } - else if (item.Type == DataType.Proto) + else if (type == DataType.Proto) { - return (item.Value as DataStoreProtoObject).Message.ToJsonString(); + return (obj as DataStoreProtoObject).Message.ToJsonString(); } else { - return item.Value.ToStringSafe(); + return obj.ToStringSafe(); } } @@ -143,5 +154,76 @@ namespace Tango.DataStore var regexItem = new Regex("^[a-zA-Z0-9_-]*$"); return regexItem.IsMatch(name); } + + /// <summary> + /// Creates a data store value from byte array that are stored in database. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="bytes">The bytes.</param> + /// <returns></returns> + /// <exception cref="System.NotSupportedException">The specified type is not supported.</exception> + public static Object CreateObject(DataType type, byte[] bytes) + { + switch (type) + { + case DataType.Int32: + return BitConverter.ToInt32(bytes, 0); + case DataType.Float: + return BitConverter.ToSingle(bytes, 0); + case DataType.Double: + return BitConverter.ToDouble(bytes, 0); + case DataType.Boolean: + return BitConverter.ToBoolean(bytes, 0); + case DataType.String: + return Encoding.Default.GetString(bytes); + case DataType.Bytes: + return bytes; + case DataType.Proto: + return DataStoreProtoObject.FromBytes(bytes); + } + + throw new NotSupportedException("The specified type is not supported."); + } + + /// <summary> + /// Creates a byte array that can be stored on database from the specified data store type and value. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="obj">The object.</param> + /// <returns></returns> + /// <exception cref="System.NotSupportedException"> + /// DataStoreProtoObject + /// or + /// The specified type is not supported. + /// </exception> + public static byte[] CreateBytes(DataType type, Object obj) + { + switch (type) + { + case DataType.Int32: + return BitConverter.GetBytes((int)obj); + case DataType.Float: + return BitConverter.GetBytes((float)obj); + case DataType.Double: + return BitConverter.GetBytes((double)obj); + case DataType.Boolean: + return BitConverter.GetBytes((bool)obj); + case DataType.String: + return Encoding.Default.GetBytes(obj.ToString()); + case DataType.Bytes: + return (byte[])obj; + case DataType.Proto: + if (obj is DataStoreProtoObject protoMessage) + { + return protoMessage.ToBytes(); + } + else + { + throw new NotSupportedException($"Data type is 'Proto' but object is not of type '{nameof(DataStoreProtoObject)}'."); + } + } + + throw new NotSupportedException("The specified type is not supported."); + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/Images/configuration.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/Images/configuration.png Binary files differindex f46a49d23..9366f42d6 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/Images/configuration.png +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/Images/configuration.png diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/Views/SelectionView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/Views/SelectionView.xaml index 51039ca89..a2aa7240b 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/Views/SelectionView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/Views/SelectionView.xaml @@ -21,7 +21,7 @@ </TextBlock> <StackPanel Margin="0 40 0 0" Width="500" HorizontalAlignment="Center"> - <Image Source="../Images/configuration.png" Stretch="None" /> + <Image Source="../Images/configuration.png" Stretch="None" Opacity="0.8" /> <TextBlock Margin="60" HorizontalAlignment="Center" FontWeight="SemiBold" Foreground="{StaticResource FSE_PrimaryAccentBrush}" FontSize="{StaticResource FSE_LargerFontSize}">Select Machine</TextBlock> <autoComplete:AutoCompleteTextBox material:HintAssist.Hint="Serial Number" MaxPopupHeight="300" Margin="0 5 0 0" Provider="{Binding MachinesAutoCompleteProvider}" SelectedItem="{Binding SelectedMachine,Mode=TwoWay}" DisplayMember="SerialNumber"> diff --git a/Software/Visual_Studio/FSE/Tango.FSE.BL/Services/MachineConfigurationService.cs b/Software/Visual_Studio/FSE/Tango.FSE.BL/Services/MachineConfigurationService.cs index a30198ff2..5b7991f44 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.BL/Services/MachineConfigurationService.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.BL/Services/MachineConfigurationService.cs @@ -7,6 +7,8 @@ using Tango.BL; using Tango.BL.Entities; using System.Data.Entity; using Tango.BL.Builders; +using Tango.BL.DTO; +using Tango.BL.Enumerations; namespace Tango.FSE.BL.Services { @@ -14,6 +16,8 @@ namespace Tango.FSE.BL.Services { public class MachineEditingComposition : IDisposable { + internal MachineDTO PreviousMachineState { get; set; } + public Machine Machine { get; set; } public List<Organization> Organizations { get; set; } public List<HardwareVersion> HardwareVersions { get; set; } @@ -36,7 +40,7 @@ namespace Tango.FSE.BL.Services public Task<MachineEditingComposition> GetMachineEditingComposition(String machineGuid) { - return Task.Factory.StartNew<MachineEditingComposition>(() => + return Task.Factory.StartNew<MachineEditingComposition>(() => { ObservablesContext db = ObservablesContext.CreateDefault(); @@ -58,6 +62,9 @@ namespace Tango.FSE.BL.Services int meters = (int)jobRuns.Select(x => x.EndPosition).Sum(); composition.TotalDyeMeters = $"{meters.ToString("N0")} meters"; + //Store current machine state for action log. + composition.PreviousMachineState = MachineDTO.FromObservable(composition.Machine); + composition.Context = db; return composition; @@ -68,7 +75,20 @@ namespace Tango.FSE.BL.Services { return Task.Factory.StartNew<MachineEditingComposition>(() => { - composition.Machine.SetupFpga = composition.Machine.SetupFirmware; + Authentication.ThrowIfNoPermission(Permissions.FSE_RunConfigurationModule); + ConnectivityProvider.ThrowIfNoInternet(); + + composition.Machine.SetupFpga = composition.Machine.SetupFirmware; //Unify setup FPGA with setup firmware. + + try + { + var machineAfterUpdateDTO = MachineDTO.FromObservable(composition.Machine); + ActionLogManager.InsertLog(ActionLogType.MachineSaved, CurrentUser, composition.Machine.SerialNumber, composition.PreviousMachineState, machineAfterUpdateDTO, "Machine saved via FSE."); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error inserting machine save action log."); + } composition.Context.SaveChanges(); composition.Context.Dispose(); @@ -78,10 +98,12 @@ namespace Tango.FSE.BL.Services public Task<int> ResetCounters(String machineGuid) { - return Task.Factory.StartNew<int>(() => + return Task.Factory.StartNew<int>(() => { using (ObservablesContext db = new ObservablesContext()) { + var sn = db.Machines.SingleOrDefault(x => x.Guid == machineGuid).SerialNumber; + ActionLogManager.InsertLog(ActionLogType.MachineCountersReset, CurrentUser.Guid, sn, machineGuid, "Machine counters reset via FSE."); return db.JobRuns.Where(x => x.MachineGuid == machineGuid).DeleteFromQuery(); } }); diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/DataStore/DefaultDataStoreProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/DataStore/DefaultDataStoreProvider.cs index 7b8e6684d..f6ea90378 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/DataStore/DefaultDataStoreProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/DataStore/DefaultDataStoreProvider.cs @@ -4,8 +4,10 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.BL; +using Tango.BL.ActionLogs; using Tango.BL.DTO; using Tango.BL.Entities; +using Tango.BL.Enumerations; using Tango.Core; using Tango.Core.DI; using Tango.DataStore; @@ -13,6 +15,7 @@ using Tango.DataStore.Editing; using Tango.DataStore.EF; using Tango.DataStore.Remote; using Tango.FSE.BL; +using Tango.FSE.Common.Authentication; using Tango.FSE.Common.Connection; using Tango.FSE.Common.DataStore; @@ -26,6 +29,12 @@ namespace Tango.FSE.UI.DataStore [TangoInject] private FSEServicesContainer Services { get; set; } + [TangoInject] + protected IActionLogManager ActionLogManager { get; set; } + + [TangoInject] + protected IAuthenticationProvider AuthenticationProvider { get; set; } + private bool _acceptFirmwareChanges; public bool AcceptFirmwareChanges { @@ -227,6 +236,11 @@ namespace Tango.FSE.UI.DataStore List<SaveModel> globals = new List<SaveModel>(); List<SaveModel> locals = new List<SaveModel>(); List<SaveModel> deleted = new List<SaveModel>(); + + List<DataStoreItemDTO> actionLogInserted = new List<DataStoreItemDTO>(); + List<Tuple<DataStoreItemDTO, DataStoreItemDTO>> actionLogModified = new List<Tuple<DataStoreItemDTO, DataStoreItemDTO>>(); + List<DataStoreItemDTO> actionLogDeleted = new List<DataStoreItemDTO>(); + UpdateDataStoreRequest updateRequest = new UpdateDataStoreRequest(); foreach (var collection in model.Collections.Where(x => !x.IsDeleted)) @@ -250,6 +264,8 @@ namespace Tango.FSE.UI.DataStore using (ObservablesContext db = ObservablesContext.CreateDefault()) { + var machine = db.Machines.SingleOrDefault(x => x.Guid == machineGuid); + var allItems = db.DataStoreItems.Where(x => x.MachineGuid == machineGuid).ToList(); //Deleted collections @@ -263,6 +279,7 @@ namespace Tango.FSE.UI.DataStore itemDb.LastUpdated = DateTime.UtcNow; itemDb.IsSynchronized = false; updateRequest.ToDelete.Add(itemDb.Guid); + actionLogDeleted.Add(DataStoreItemDTO.FromObservable(itemDb)); } } } @@ -277,6 +294,7 @@ namespace Tango.FSE.UI.DataStore itemDb.LastUpdated = DateTime.UtcNow; itemDb.IsSynchronized = false; updateRequest.ToDelete.Add(itemDb.Guid); + actionLogDeleted.Add(DataStoreItemDTO.FromObservable(itemDb)); } } @@ -296,11 +314,14 @@ namespace Tango.FSE.UI.DataStore itemDb.Value = EFDataStoreHelper.CreateBytes(item.Item.Type, item.Item.Value); db.DataStoreItems.Add(itemDb); updateRequest.ToUpsert.Add(DataStoreItemDTO.FromObservable(itemDb)); + actionLogInserted.Add(DataStoreItemDTO.FromObservable(itemDb)); } else //update local item only if changed... { bool upsert = MachineProvider.IsPPCAvailable && !item.Item.ExistsOnMachine; + var itemDbDTO = DataStoreItemDTO.FromObservable(itemDb); + if (itemDb.IsDeleted) //restore if item was deleted.. { itemDb.IsDeleted = false; @@ -324,6 +345,7 @@ namespace Tango.FSE.UI.DataStore if (upsert) { updateRequest.ToUpsert.Add(DataStoreItemDTO.FromObservable(itemDb)); + actionLogModified.Add(new Tuple<DataStoreItemDTO, DataStoreItemDTO>(itemDbDTO, DataStoreItemDTO.FromObservable(itemDb))); } } } @@ -338,6 +360,33 @@ namespace Tango.FSE.UI.DataStore } db.SaveChanges(); + + try + { + //Save action logs.. + actionLogDeleted = actionLogDeleted.DistinctBy(x => x.Guid).ToList(); + actionLogInserted = actionLogInserted.DistinctBy(x => x.Guid).ToList(); + actionLogModified = actionLogModified.DistinctBy(x => x.Item1.Guid).ToList(); + + foreach (var item in actionLogDeleted) + { + ActionLogManager.InsertLog(ActionLogType.DataStoreItemDeleted, AuthenticationProvider.CurrentUser,$"{machine.SerialNumber} => {item.Key}", item, "Data store item deleted via FSE.", true); + } + + foreach (var item in actionLogInserted) + { + ActionLogManager.InsertLog(ActionLogType.DataStoreItemCreated, AuthenticationProvider.CurrentUser, $"{machine.SerialNumber} => {item.Key}", item, "Data store item created via FSE.", false); + } + + foreach (var item in actionLogModified) + { + ActionLogManager.InsertLog(ActionLogType.DataStoreItemModified, AuthenticationProvider.CurrentUser, $"{machine.SerialNumber} => {item.Item1.Key}", item.Item1, item.Item2, "Data store item modified via FSE."); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error saving action logs for data store updates."); + } } return GetDataStoreModel(machineGuid).Result; diff --git a/Software/Visual_Studio/Tango.BL/DTO/DataStoreItemDTO.cs b/Software/Visual_Studio/Tango.BL/DTO/DataStoreItemDTO.cs index 475c9f648..3262970a1 100644 --- a/Software/Visual_Studio/Tango.BL/DTO/DataStoreItemDTO.cs +++ b/Software/Visual_Studio/Tango.BL/DTO/DataStoreItemDTO.cs @@ -4,11 +4,35 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.BL.Entities; +using Tango.DataStore; namespace Tango.BL.DTO { public class DataStoreItemDTO : DataStoreItemDTOBase { + protected override string OnGetActionLogName() + { + return $"'{CollectionName}' => '{Key}'"; + } + public DataType Type { get; set; } + + public String Val { get; set; } + + protected override void OnFromObservableCompleted(DataStoreItem observable) + { + base.OnFromObservableCompleted(observable); + Type = (DataType)observable.DataType; + Val = DataStoreHelper.FormatDataStoreValue(Type, DataStoreHelper.CreateObject(Type, observable.Value)); + } + + protected override bool OnShouldActionLogIgnore(string propName) + { + return + propName == nameof(Value) || + propName == nameof(DataType) || + propName == nameof(IsSynchronized); + } } } diff --git a/Software/Visual_Studio/Tango.BL/Enumerations/ActionLogType.cs b/Software/Visual_Studio/Tango.BL/Enumerations/ActionLogType.cs index e0ba5d586..868bf7915 100644 --- a/Software/Visual_Studio/Tango.BL/Enumerations/ActionLogType.cs +++ b/Software/Visual_Studio/Tango.BL/Enumerations/ActionLogType.cs @@ -96,5 +96,17 @@ namespace Tango.BL.Enumerations //Firmware [Description("Firmware Upgraded")] FirmwareUpgraded = 800, + + //Job Runs + [Description("Machine Counters Reset")] + MachineCountersReset = 900, + + //Data Store + [Description("Data Store Item Created")] + DataStoreItemCreated = 1000, + [Description("Data Store Item Modified")] + DataStoreItemModified = 1001, + [Description("Data Store Item Deleted")] + DataStoreItemDeleted = 1002, } } diff --git a/Software/Visual_Studio/Tango.BL/ObservableEntityDTO.cs b/Software/Visual_Studio/Tango.BL/ObservableEntityDTO.cs index 9b8aa0687..1c3edac82 100644 --- a/Software/Visual_Studio/Tango.BL/ObservableEntityDTO.cs +++ b/Software/Visual_Studio/Tango.BL/ObservableEntityDTO.cs @@ -106,6 +106,8 @@ namespace Tango.BL } } + dto.OnFromObservableCompleted(observable); + return dto; } @@ -319,5 +321,14 @@ namespace Tango.BL { return this.GetType().Name; } + + /// <summary> + /// Called when the static method <see cref="FromObservable(T)"/> completes. + /// </summary> + /// <param name="observable">The observable.</param> + protected virtual void OnFromObservableCompleted(T observable) + { + //Just for override + } } } diff --git a/Software/Visual_Studio/Tango.BL/Tango.BL.csproj b/Software/Visual_Studio/Tango.BL/Tango.BL.csproj index 1281c4949..4da8606f7 100644 --- a/Software/Visual_Studio/Tango.BL/Tango.BL.csproj +++ b/Software/Visual_Studio/Tango.BL/Tango.BL.csproj @@ -597,6 +597,10 @@ </None> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\DataStore\Tango.DataStore\Tango.DataStore.csproj"> + <Project>{e0364dfa-0721-4637-9d32-9d22aac109d6}</Project> + <Name>Tango.DataStore</Name> + </ProjectReference> <ProjectReference Include="..\SideChains\ColorMine\ColorMine.csproj"> <Project>{37e4ceab-b54b-451f-b535-04cf7da9c459}</Project> <Name>ColorMine</Name> @@ -641,7 +645,7 @@ </Target> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" /> + <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" /> </VisualStudio> </ProjectExtensions> </Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/Utilities/Tango.JobRunsGenerator/Program.cs b/Software/Visual_Studio/Utilities/Tango.JobRunsGenerator/Program.cs index 11c54cf64..71d5779a6 100644 --- a/Software/Visual_Studio/Utilities/Tango.JobRunsGenerator/Program.cs +++ b/Software/Visual_Studio/Utilities/Tango.JobRunsGenerator/Program.cs @@ -19,7 +19,7 @@ namespace Tango.JobRunsGenerator static void Main(string[] args) { DataSource dataSource = new DataSource(); - dataSource.Catalog = "Tango_TEST"; + dataSource.Catalog = "Tango_DEV"; dataSource.Address = "twine.database.windows.net"; dataSource.IntegratedSecurity = false; dataSource.UserName = "Roy"; |
