using Google.Protobuf; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Tango.BL; using Tango.BL.Entities; using Tango.BL.Enumerations; using Tango.Core; using Tango.Integration.ExternalBridge; using Tango.PMR.Diagnostics; using Tango.Integration.Operation; using Tango.PPC.Common.Application; using Tango.PPC.Common.Authentication; using Tango.PPC.Common.Connection; using Tango.Transport; using Tango.PPC.Common.ExternalBridge; using Tango.PPC.Shared.Events; using Tango.Core.DI; namespace Tango.PPC.Common.EventLogging { /// /// Represents the default database events logger. /// /// public class DefaultEventLogger : ExtendedObject, IEventLogger { private ObservablesContext _db; private Thread _logThread; private ConcurrentQueue _events; private IMachineProvider _machineProvider; private IAuthenticationProvider _authentication; private Dictionary _eventTypesGuids; private String _hostName; private bool _isInitialized; private List _pendingEvents; private List _currentEvents; private Machine _machine; #region Events /// /// Occurs when a new machine event has been received. /// public event EventHandler EventReceived; /// /// Occurs when a machine event has been resolved. /// public event EventHandler EventResolved; #endregion private IPPCExternalBridgeService _externalBridge; [TangoInject(Mode = TangoInjectMode.WhenAvailable)] public IPPCExternalBridgeService ExternalBridgeService { get { return _externalBridge; } set { if (_externalBridge != value) { _externalBridge = value; _externalBridge.RegisterRequestHandler(this); } } } #region Constructors /// /// Initializes a new instance of the class. /// /// The application manager. /// The authentication provider. public DefaultEventLogger(IMachineProvider machineProvider, IAuthenticationProvider authenticationProvider) { _hostName = Environment.MachineName; _events = new ConcurrentQueue(); _pendingEvents = new List(); _currentEvents = new List(); _eventTypesGuids = new Dictionary(); _machineProvider = machineProvider; _authentication = authenticationProvider; _logThread = new Thread(LogThreadMethod); _logThread.IsBackground = true; _logThread.Start(); _machineProvider.MachineOperator.MachineEventsStateProvider.NewEvents += MachineEventsStateProvider_NewEvents; _machineProvider.MachineOperator.MachineEventsStateProvider.EventsResolved += MachineEventsStateProvider_EventsResolved; _machineProvider.MachineOperator.RequestSent += Machine_RequestSent; _machineProvider.MachineOperator.RequestFailed += Machine_RequestFailed; _machineProvider.MachineOperator.ResponseReceived += Machine_ResponseReceived; _machineProvider.MachineOperator.StateChanged += MachineOperator_StateChanged; _machineProvider.MachineOperator.PrintingStarted += MachineOperator_PrintingStarted; _machineProvider.MachineOperator.PrintingAborted += MachineOperator_PrintingAborted; _machineProvider.MachineOperator.PrintingCompleted += MachineOperator_PrintingCompleted; _machineProvider.MachineOperator.PrintingFailed += MachineOperator_PrintingFailed; } #endregion #region Private Methods private void Init() { if (!_isInitialized) { try { _db = ObservablesContext.CreateDefault(); _machine = _db.Machines.FirstOrDefault(); _db.EventTypes.ToList(); foreach (var type in _db.EventTypes) { try { _eventTypesGuids.Add((EventTypes)type.Code, type); } catch (Exception ex) { LogManager.Log(ex, $"Error initializing event type '{type.Name}'."); } } _isInitialized = true; } catch (Exception ex) { _isInitialized = false; LogManager.Log(ex, "Error initializing event types."); } } } #endregion #region Event Handlers /// /// Handles the PrintingFailed event of the MachineOperator. /// /// The source of the event. /// The instance containing the event data. private void MachineOperator_PrintingFailed(object sender, PrintingFailedEventArgs e) { EventTypes? relatedEventType = null; if (e.Exception is Transport.ResponseErrorException responseError) { if (responseError.Container.EventCode > 0) { relatedEventType = (EventTypes)responseError.Container.EventCode; } } Log(EventTypes.JOB_FAILED, e.Exception.Message, relatedEventType); } /// /// Handles the PrintingCompleted event of the MachineOperator. /// /// The source of the event. /// The instance containing the event data. private void MachineOperator_PrintingCompleted(object sender, PrintingEventArgs e) { Log(EventTypes.JOB_COMPLETED, $"Job '{e.Job.Name}' completed successfully."); } /// /// Handles the PrintingAborted event of the MachineOperator. /// /// The source of the event. /// The instance containing the event data. private void MachineOperator_PrintingAborted(object sender, PrintingEventArgs e) { Log(EventTypes.JOB_ABORTED, $"Job '{e.Job.Name}' has been aborted."); } /// /// Handles the PrintingStarted event of the MachineOperator. /// /// The source of the event. /// The instance containing the event data. private void MachineOperator_PrintingStarted(object sender, PrintingEventArgs e) { Log(EventTypes.JOB_STARTED, $"Job '{e.Job.Name}' started."); } /// /// Handles the machine operator state changed event. /// /// The sender. /// The e. private void MachineOperator_StateChanged(object sender, Transport.TransportComponentState e) { if (e == Transport.TransportComponentState.Connected) { _events = new ConcurrentQueue(); _currentEvents.Clear(); } } /// /// Handles the RequestSent event of the connected machine. /// /// The sender. /// The message. private void Machine_RequestSent(object sender, IMessage message) { //Log(EventTypes.REQUEST_SENT, String.Format("Sending request '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); } /// /// Handles the RequestFailed event of the connected machine. /// /// The source of the event. /// The instance containing the event data. private void Machine_RequestFailed(object sender, RequestFailedEventArgs e) { //Log(EventTypes.REQUEST_FAILED, String.Format("Request failed '{0}'...{1}{2}{1}{3}", e.Message.GetType().Name, Environment.NewLine, e.Message.ToJsonString(), e.Exception.ToString())); } /// /// Handles the ResponseReceived event of the connected machine. /// /// The sender. /// The message. private void Machine_ResponseReceived(object sender, IMessage message) { //Log(EventTypes.RESPONSE_RECEIVED, String.Format("Response received '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); } /// /// Handles the connected machine events state provider NewEvents event. /// /// The sender. /// The events. private void MachineEventsStateProvider_NewEvents(object sender, IEnumerable events) { foreach (var ev in events) { Log(ev); } } /// /// Handles the connected machine events state provider EventsResolved event. /// /// The sender. /// The events. private void MachineEventsStateProvider_EventsResolved(object sender, IEnumerable events) { foreach (var ev in events) { //Log(String.Format("Event '{0}' resolved.", ev.EventType.Name)); var existing = _currentEvents.SingleOrDefault(x => x.Type == ev.Type); if (existing != null) { _currentEvents.Remove(existing); EventResolved?.Invoke(this, existing); } } } #endregion #region Logging /// /// Logs the specified machine event. /// /// The machine event. public void Log(MachinesEvent machineEvent) { machineEvent.HostName = _hostName; machineEvent.EventType = _eventTypesGuids[machineEvent.Type]; if (_machine == null) { _pendingEvents.Add(machineEvent); } else { if (_pendingEvents.Count > 0) { var pending = _pendingEvents.ToList(); _pendingEvents.Clear(); foreach (var ev in pending) { Log(ev); } } LogManager.Log("Logging event " + machineEvent.EventType.Name); machineEvent.MachineGuid = _machine.Guid; machineEvent.UserGuid = null; machineEvent.User = null; _events.Enqueue(machineEvent); if (!_currentEvents.Exists(x => x.Type == machineEvent.Type)) { if (machineEvent.Group != EventTypeGroups.Application && machineEvent.Group != EventTypeGroups.Transport && machineEvent.Group != EventTypeGroups.Jobs) { _currentEvents.Add(machineEvent); } } EventReceived?.Invoke(this, machineEvent); } } /// /// Logs the specified event type. /// /// Type of the event. /// The message. public void Log(EventTypes eventType, string message, EventTypes? relatedEventType = null) { Init(); MachinesEvent machineEvent = new MachinesEvent(); machineEvent.DateTime = DateTime.UtcNow; machineEvent.Description = message; machineEvent.EventType = _eventTypesGuids[eventType]; machineEvent.EventTypeGuid = machineEvent.EventType.Guid; if (relatedEventType != null) { machineEvent.RelatedEventType = _eventTypesGuids[relatedEventType.Value]; } Log(machineEvent); } /// /// Logs the specified hardware event. /// /// The hardware event. public void Log(Event hardwareEvent) { Log((EventTypes)hardwareEvent.Type, hardwareEvent.Message); } /// /// Logs the specified exception using the . /// /// The exception. public void Log(Exception exception) { Log(EventTypes.APPLICATION_EXCEPTION, exception.FlattenMessage()); } /// /// Logs the specified exception using the . /// /// The exception. /// public void Log(Exception exception, string description) { Log(EventTypes.APPLICATION_EXCEPTION, description + Environment.NewLine + exception.FlattenMessage()); } /// /// Logs the specified message using the . /// /// The message. public void Log(String message) { Log(EventTypes.APPLICATION_INFORMATION, message); } /// /// Logging thread loop. /// private void LogThreadMethod() { while (true) { FlushAll(); Thread.Sleep(5000); } } /// /// Immediately saves all pending events to database. /// public void FlushAll() { bool _saveChanges = false; while (_events.Count > 0) { MachinesEvent ev = null; if (_events.TryDequeue(out ev)) { ev.User = null; _db.MachinesEvents.Add(ev); _saveChanges = true; } } if (_saveChanges) { try { _db.SaveChanges(); } catch (Exception ex) { LogManager.Log(ex, "Error saving machine event to database."); } } } #endregion #region External Bridge Handler [ExternalBridgeRequestHandlerMethod(typeof(PushEmulatedEventRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)] public async Task OnStartPerformanceUpdatesRequest(PushEmulatedEventRequest request, String token, ExternalBridgeReceiver receiver) { _machineProvider.MachineOperator.PushEmulatedEvent(request.Event, request.Timeout); await receiver.SendGenericResponse(new PushEmulatedEventResponse(), token); } public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) { } #endregion } }