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
}
}