/// <summary> /// Process our data /// </summary> /// <param name="state"></param> private static void ProcessData(object state) { object[] InVals = (object[])state; String[] InLines = (string[])InVals[0]; Type TargetType = (Type)InVals[1]; PropertyInfo[] HeaderInfo = (PropertyInfo[])InVals[2]; try { if (TargetType != null) { Array OutList = Array.CreateInstance(TargetType, InLines.Length); for (int a = 0; a < InLines.Length; a++) { OutList.SetValue(MM_Serialization.Deserialize(HeaderInfo, InLines[a].Split(','), Activator.CreateInstance(TargetType)), a); } MM_EMS_Data_Updater.ProcessUpdate(TargetType, OutList); OutList = null; } } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Error sending {0} data: {1}", (TargetType == null ? "UNKNOWN" : TargetType.Name), ex); } }
/// <summary> /// Send our message internally /// </summary> /// <param name="SystemGuid"></param> /// <param name="ProcedureName"></param> /// <param name="Parameters"></param> private static void SendMessageInternal(MethodInfo Method, MM_WCF_Interface FoundConnection, object[] parameters) { try { Method.Invoke(FoundConnection.ConversationHandler, parameters); } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Error sending {0} to {1}: {2}", Method.Name, FoundConnection.User.UserName, ex.Message); } }
/// <summary> /// Send our message internally /// </summary> /// <param name="SystemGuid"></param> /// <param name="ProcedureName"></param> /// <param name="Parameters"></param> private static void SendMessageInternal(MethodInfo Method, MM_Administrator_Types FoundConnection, object[] parameters) { try { Method.Invoke(FoundConnection.ConversationHandler, parameters); } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Error sending {0} to {1}: {2}", Method.Name, "ADMIN", ex.Message); } }
/// <summary> /// Handle a client first connecting /// </summary> /// <returns></returns> public Guid RegisterApplicationStartup(String IPAddress, int Port, String MachineName) { MM_Notification.WriteLine(ConsoleColor.Cyan, "New connection from {0} {1} on port {2}", IPAddress, MachineName, Port); Guid UserGUID = Guid.NewGuid(); this.User = new MM_User() { UserId = UserGUID, UserName = "******", LoggedOnTime = DateTime.Now, IPAddress = IPAddress, MachineName = MachineName, Port = Port, LastReceivedMessage = DateTime.Now }; User.LastReceivedMessage = DateTime.Now; MM_Server.AddUser(this); return(UserGUID); }
/// <summary> /// Handle a user login /// </summary> /// <param name="Name"></param> /// <param name="Password"></param> /// <param name="UserGuid"></param> /// <returns></returns> public string[] HandleUserLogin(string Name, string Password) { MM_Notification.WriteLine(ConsoleColor.Cyan, "User {0} attempting to log in from {1} on {2}", Name, User.IPAddress, DateTime.Now); User.UserName = Name; if (Settings.Default.AllowTestCredentials && Name == "**MacomberMapTesting**") { User.LoggedOnTime = DateTime.Now; User.ERCOTUser = true; return(new string[] { "ERCOT" }); } //Validate the user's credentials against active directory (thanks to http://stackoverflow.com/questions/290548/validate-a-username-and-password-against-active-directory) bool IsValid = false; try { using (PrincipalContext pc = new PrincipalContext(ContextType.Domain)) IsValid = pc.ValidateCredentials(Name, Password); } catch { } if (IsValid && Settings.Default.AcceptAllDomainUsers) { return new string[] { "ERCOT" } } ; if (!IsValid || !File.Exists(Properties.Settings.Default.LoginFileLocation)) { MM_Notification.WriteLine(ConsoleColor.Red, "User {0} log on from {1} failed: invalid credentials or no PERMIT area file.", Name, User.IPAddress); return(new String[0]); } //Now, check against our user names foreach (String str in File.ReadAllLines(Properties.Settings.Default.LoginFileLocation)) { if (str.StartsWith(Name + ",", StringComparison.CurrentCultureIgnoreCase)) { User.LoggedOnTime = DateTime.Now; String[] PermissionAreas = str.Substring(str.IndexOf(',') + 1).Split(','); User.ERCOTUser = PermissionAreas.Contains("ERCOT"); return(PermissionAreas); } } //We didn't find our user, so log out. MM_Notification.WriteLine(ConsoleColor.Red, "User {0} log on from {1} failed: not found in PERMIT area file.", Name, User.IPAddress); return(new string[0]); }
/// <summary> /// Read our XML configuration file /// </summary> public static void DetermineFileTypes() { //Add in our list of file paths foreach (Type FoundType in typeof(MM_Line_Data).Assembly.GetTypes()) { UpdateCommandAttribute UpdateCommand = null; RetrievalCommandAttribute RetrievalCommand = null; FileNameAttribute FileName = null; foreach (object obj in FoundType.GetCustomAttributes(false)) { if (obj is UpdateCommandAttribute) { UpdateCommand = (UpdateCommandAttribute)obj; } else if (obj is FileNameAttribute) { FileName = (FileNameAttribute)obj; } else if (obj is RetrievalCommandAttribute) { RetrievalCommand = (RetrievalCommandAttribute)obj; } } if (UpdateCommand != null && FileName != null) { MM_EMSReader_TCP.InputTypes.Add(FileName.FileName, FoundType); MM_EMSReader_TCP.UpdateCommands.Add(FileName.FileName, UpdateCommand.UpdateCommand); if (!String.IsNullOrEmpty(Settings.Default.TEDESourceFolder)) { MM_EMSReader_File.FileInfo.Add(FileName.FileName, new MM_EMSReader_File(Path.Combine(Settings.Default.TEDESourceFolder, FileName.FileName), UpdateCommand.UpdateCommand, FoundType)); } } // FileInfo.Add(FileName.FileName, new MM_EMSReader_FileInformation(Path.Combine(Settings.Default.SourceFolder,FileName.FileName), UpdateCommand.UpdateCommand, FoundType)); //Check our update command, make sure it's okay if (UpdateCommand != null && FileName != null && typeof(IMM_ConversationMessage_Types).GetMethod(UpdateCommand.UpdateCommand) == null) { MM_Notification.WriteLine(ConsoleColor.Yellow, "Unable to find update command {0} for type {1}", UpdateCommand.UpdateCommand, FileName.FileName); } if (RetrievalCommand != null && FileName != null && typeof(IMM_EMS_Types).GetMethod(RetrievalCommand.RetrievalCommand) == null) { MM_Notification.WriteLine(ConsoleColor.Yellow, "Unable to find retrieval command {0} for type {1}", RetrievalCommand.RetrievalCommand, FileName.FileName); } } }
/// <summary> /// Retrieve our file, and create our output /// </summary> /// <returns></returns> public void ReadFileAndSendResults(object state) { while (true) { try { //First, wait for our trigger. If we timed out, set it, so that our next run works properly. bool TriggerTimeout = !ResetEvent.WaitOne(TimeSpan.FromSeconds(55)); //Thread.Sleep(250); //Now, read our file bool Success = false; int RetryCount = 0; FileInfo fI = new FileInfo(FileName); if (fI.Exists && fI.Length > 0 && (fI.LastWriteTime != LastFileWrite || TriggerTimeout)) { LastFileWrite = fI.LastWriteTime; do { try { DateTime StartTime = DateTime.Now; using (FileStream fS = WaitForFile(FileName, FileMode.Open, FileAccess.Read, FileShare.Read)) if (fS != null) { using (StreamReader sRd = new StreamReader(fS, new UTF8Encoding(false))) ProcessStreamRead(sRd, FileName, TargetType, ref LatestBatchID); } Success = true; } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Error reading {0}: {1}", FileName, ex.Message); RetryCount++; } } while (!Success && RetryCount < 2); fI = null; } ResetEvent.Reset(); } catch (Exception ex2) { MM_Notification.WriteLine(ConsoleColor.Red, "Thread for {0} shutting down: {1}", FileName, ex2); } } }
/// <summary> /// Start our listener /// </summary> /// <param name="state"></param> private static void StartListener(object state) { Listener = new TcpListener(IPAddress.Any, Properties.Settings.Default.TCPListener); Listener.Start(); MM_Notification.WriteLine(ConsoleColor.Green, "EMS: Listening for incoming TCP connections on {0}", Listener.LocalEndpoint); while (true) { try { ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessTCPMessage), Listener.AcceptTcpClient()); } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "EMS: Unable to accept an incoming connection: {0}", ex); } } }
/// <summary> /// Start our History listener /// </summary> /// <param name="state"></param> private static void StartHistoryListener(object state) { //Load our XML configuration, and start up our History server try { HistoryServer = MM_History_Server.FindServer(Settings.Default.HistoryServer); HistoryServer.Open(); MM_Notification.WriteLine(ConsoleColor.Green, "History: Connected to History Server {0}", HistoryServer.Name); } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "History: Unable to connect to History server: {0}", ex.ToString()); } //Now, create our Historype and handle appropriately StartTime = DateTime.Now; while (true) { mreQueryReady.Reset(); //Check whether there are any queries worth running and pruning int RanQueries = 0, PrunedQueries = 0; foreach (MM_Historic_Query Query in Queries.Values.ToArray()) { if (Query.State == MM_Historic_Query.enumQueryState.Queued) { Query.State = MM_Historic_Query.enumQueryState.InProgress; RanQueries++; ThreadPool.QueueUserWorkItem(new WaitCallback(RunQuery), Query); } else if (Query.State == MM_Historic_Query.enumQueryState.Retrieved) { PrunedQueries++; Queries.Remove(Query.Id); } } MM_Notification.WriteLine(ConsoleColor.Green, "History: Ran {0} queries, removed {1} queries.", RanQueries, PrunedQueries); mreQueryReady.WaitOne(); } }
/// <summary> /// Open our database connection /// </summary> private static void OpenDatabaseConnection() { String ConnectionParameter = Settings.Default.DatabaseConnectionString; if (!String.IsNullOrEmpty(Settings.Default.DatabaseEncryptedPassword)) { ConnectionParameter = ConnectionParameter.Replace("[Password]", MM_Encryption.Decrypt(Settings.Default.DatabaseEncryptedPassword)); } Assembly DatabaseAssembly = Assembly.Load(Settings.Default.DatabaseAssembly); oConn = (DbConnection)Activator.CreateInstance(DatabaseAssembly.GetType(Settings.Default.DatabaseConnectionType)); oConn.ConnectionString = Settings.Default.DatabaseConnectionString; try { oConn.Open(); MM_Notification.WriteLine(ConsoleColor.Green, "Database: Connected to {0} ({1}).", oConn.DataSource, oConn.ServerVersion); } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Database: Error connecting to {0}: {1}", oConn.DataSource, ex); } }
/// <summary> /// Send a command /// </summary> /// <param name="Command"></param> /// <param name="OldValue"></param> /// <returns></returns> public bool SendCommand(string Command, String OldValue) { byte[] OutBytes = new UTF8Encoding(false).GetBytes("#CMD_TYPE,APP,FAM,CMD_NAME,DB,RECORD,FIELD,TEID,VALUE" + Environment.NewLine + Command + Environment.NewLine); DateTime SimTime = MM_Server.SimulatorTimeData.Length == 0 ? DateTime.Now : MM_Server.SimulatorTimeData[0].Simulation_Time; if (!String.IsNullOrEmpty(Settings.Default.OperatorCommandTCPAddress)) { try { using (TcpClient Client = new TcpClient()) { Client.Connect(Settings.Default.OperatorCommandTCPAddress, Settings.Default.OperatorCommandTCPPort); using (NetworkStream nS = Client.GetStream()) nS.Write(OutBytes, 0, OutBytes.Length); MM_Notification.WriteLine(ConsoleColor.White, "Sent {1} command to TEDE/TCP: by {0} on {2}. SimTime {3}", User.UserName, Command, DateTime.Now, SimTime); MM_Database_Connector.LogCommand(Command, User, "TCP"); } } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Error sending {1} command to TEDE/TCP: by {0} on {2} SimTime {3}: {4} ", User.UserName, Command, DateTime.Now, SimTime, ex); return(false); } } if (!String.IsNullOrEmpty(Properties.Settings.Default.OperatorCommandPath)) { String[] splStr = Command.TrimEnd(',').Split(','); String TargetFileName = Path.Combine(Properties.Settings.Default.OperatorCommandPath, ((splStr[0].Equals("REFRESH")) ? "EMS_REFRESH_" : "EMS_COMMANDS_") + RandomGenerator.Next().ToString() + ".csv"); using (FileStream fS = new FileStream(TargetFileName, FileMode.CreateNew, FileAccess.Write)) fS.Write(OutBytes, 0, OutBytes.Length); MM_Notification.WriteLine(ConsoleColor.White, "Sent {1} command to TEDE/File: by {0} on {2}. SimTime {3}", User.UserName, Command, DateTime.Now, SimTime); MM_Database_Connector.LogCommand(Command, User, "File"); } //Now, write out our command MM_EMS_Command[] OutCommands = new MM_EMS_Command[] { new MM_EMS_Command(Command, User.UserName, User.MachineName, SimTime, OldValue) }; try { List <String> OutLines = new List <string>(); if (!File.Exists("EMSCommands.csv")) { OutLines.Add(MM_EMS_Command.HeaderLine()); } OutLines.Add(OutCommands[0].BuildLine()); File.AppendAllLines("EMSCommands.csv", OutLines.ToArray()); } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Unable to write out command to local log: {0}", ex); } //Now, send our commands to all administrators if (Properties.Settings.Default.ForwardCommandsToAdminClients) { foreach (MM_Administrator_Types Interface in MM_Server.ConnectedAdministatorList) { try { MM_Administrator_Types.SendMessageToConsole(Interface.ConversationGuid, "AddEMSCommands", new object[] { OutCommands }); } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Unable to send {0} to Admin: {1}", "AddEMSCommands", ex); } } } //Now, send out our commands to all clients if (Properties.Settings.Default.ForwardCommandsToERCOTClients) { foreach (MM_WCF_Interface Interface in MM_Server.ConnectedUserList) { if (Interface.User.LoggedOnTime != default(DateTime) && Interface.User.ERCOTUser) { try { MM_WCF_Interface.SendMessage(Interface.User.UserId, "AddEMSCommands", new object[] { OutCommands }); } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Unable to send {0} to {1}: {2}", "AddEMSCommands", Interface.User.UserName, ex); } } } } User.LastCommand = DateTime.Now; return(true); }
/// <summary> /// Process an incoming client /// </summary> /// <param name="state"></param> private static void ProcessTCPMessage(object state) { //Now, notify that we have a connection, let's create our stream reader around it, and retrieve our header TcpClient Conn = (TcpClient)state; String FileName = ""; DateTime StartTime = DateTime.Now; //If our source IP address changes, notify the console IPAddress Address = ((IPEndPoint)Conn.Client.RemoteEndPoint).Address; if (Address.ToString() != MM_Server.LastTEDEAddress.ToString()) { MM_Notification.WriteLine(ConsoleColor.Yellow, "TEDE: IP Source Address change detected: {0}, last {1}", Address, MM_Server.LastTEDEAddress); } MM_Server.LastTEDEAddress = Address; using (NetworkStream nRd = Conn.GetStream()) using (StreamReader sRd = new StreamReader(nRd)) try { //Define our key variables List <String> OutList = new List <string>(); bool AtBeginning = true; PropertyInfo[] HeaderInfo = null; DateTime BatchId = default(DateTime); Type TargetType = null; //Now, read in all of our lines of data, using reflection to store our data String InLine; while ((InLine = sRd.ReadLine()) != null) { if (InLine.StartsWith("#")) { String[] splStr = InLine.TrimStart('#').TrimEnd(',').Split(','); MM_Server.LastTEDEUpdate = DateTime.Now; //If we're at the beginning, process accordingly if (AtBeginning) { StartTime = DateTime.Now; //Pull in, and parse our first line. BatchId = new DateTime(1970, 1, 1).AddSeconds(Convert.ToDouble(splStr[0])); FileName = splStr[1]; if (!FileName.EndsWith(".csv", StringComparison.CurrentCultureIgnoreCase)) { FileName += ".csv"; } if (InputTypes.TryGetValue(FileName, out TargetType)) { //Now that we have our first, line, build our outgoing list OutList.Clear(); //Read our header String[] HeaderLine = sRd.ReadLine().TrimStart('#').Split(','); HeaderInfo = MM_Serialization.GetHeaderInfo(TargetType, HeaderLine); for (int a = 0; a < HeaderLine.Length; a++) { if (HeaderInfo[a] == null) { MM_Notification.WriteLine(ConsoleColor.Yellow, "Unknown variable {0} in {1}", HeaderLine[a], FileName); } } //Confirm all of our headers are present foreach (PropertyInfo pI in TargetType.GetProperties()) { if (Array.FindIndex <String>(HeaderLine, T => T.Equals(pI.Name, StringComparison.CurrentCultureIgnoreCase)) == -1) { MM_Notification.WriteLine(ConsoleColor.Yellow, "Missing variable {0} ({1}) in {2} / {3}", pI.Name, pI.PropertyType.Name, TargetType.Name, FileName); } } } else { //Console.WriteLine("Rereading file " + FileName); sRd.ReadLine(); } AtBeginning = false; } else { DateTime EndTime = new DateTime(1970, 1, 1).AddSeconds(Convert.ToDouble(splStr[0])); if (EndTime != BatchId) { MM_Notification.WriteLine(ConsoleColor.Red, "Mismatch date on {0}: Start {1}, End {2}", FileName, BatchId, EndTime); } if (OutList != null) { ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessData), new object[] { OutList.ToArray(), TargetType, HeaderInfo }); } else { //MM_Notification.WriteLine(ConsoleColor.Yellow, "Ignore processing data from {0}.", FileName); } AtBeginning = true; } } else if (OutList != null) { OutList.Add(InLine); } } } catch (Exception ex) { MM_Notification.WriteLine(ConsoleColor.Red, "Error reading {0} from {1}: {2}", FileName, Conn.Client.RemoteEndPoint, ex); } }
/// <summary> /// Process a stream, reading and parsing its content /// </summary> /// <param name="InLines"></param> /// <param name="FileName"></param> public static void ProcessStreamRead(StreamReader sRd, String FileName, Type TargetType, ref DateTime LatestBatchID) { //Now, parse our stream IList OutList = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(TargetType)); //Read our header String FirstLine = sRd.ReadLine().TrimStart('#').TrimEnd(','); String[] HeaderLine; Double ProperDate; DateTime BatchId = DateTime.Now; if (Double.TryParse(FirstLine, out ProperDate)) { BatchId = new DateTime(1970, 1, 1).AddSeconds(ProperDate); HeaderLine = sRd.ReadLine().TrimStart('#').Split(','); } else { HeaderLine = FirstLine.Split(','); } if (BatchId < LatestBatchID) { MM_Notification.WriteLine(ConsoleColor.Red, "Aborting {0} Batch {1} because it's older than {1}", FileName, BatchId, LatestBatchID); return; } else { LatestBatchID = BatchId; } PropertyInfo[] HeaderInfo = MM_Serialization.GetHeaderInfo(TargetType, HeaderLine); for (int a = 0; a < HeaderLine.Length; a++) { if (HeaderInfo[a] == null) { MM_Notification.WriteLine(ConsoleColor.Yellow, "Unknown variable {0} in {1}", HeaderLine[a], FileName); } } //Confirm all of our headers are present foreach (PropertyInfo pI in TargetType.GetProperties()) { if (Array.FindIndex <String>(HeaderLine, T => T.Equals(pI.Name, StringComparison.CurrentCultureIgnoreCase)) == -1) { MM_Notification.WriteLine(ConsoleColor.Yellow, "Missing variable {0} ({1}) in {2} / {3}", pI.Name, pI.PropertyType.Name, TargetType.Name, FileName); } } //Now, read in all of our lines of data, using reflection to store our data String InLine; while ((InLine = sRd.ReadLine()) != null) { if (InLine.StartsWith("#")) { DateTime EndTime = new DateTime(1970, 1, 1).AddSeconds(Convert.ToDouble(InLine.TrimStart('#').TrimEnd(','))); if (EndTime != BatchId) { MM_Notification.WriteLine(ConsoleColor.Red, "Mismatch date on {0}: Start {1}, End {2}", FileName, BatchId, EndTime); } } else { Object OutObj = MM_Serialization.Deserialize(HeaderInfo, InLine.Split(','), Activator.CreateInstance(TargetType)); //if (OutObj is MacomberMapCommunications.Messages.EMS.MM_BreakerSwitch_Data) //{ // MacomberMapCommunications.Messages.EMS.MM_BreakerSwitch_Data bs = (MacomberMapCommunications.Messages.EMS.MM_BreakerSwitch_Data)OutObj; // if (bs.TEID_CB == 118964) // MM_Notification.WriteLine(ConsoleColor.Magenta, " TEID=" + bs.TEID_CB.ToString() + " status=" + (bs.Open_CB ? "Open" : "Closed")); //} OutList.Add(OutObj); } } //Once our data are done, use the interprocess communication to update our data if (TargetType == typeof(MM_EMS_Command)) { MM_Server.EMSCommands.Clear(); foreach (Object obj in OutList) { MM_Server.EMSCommands.Add((MM_EMS_Command)obj); } } else { MM_EMS_Data_Updater.ProcessUpdate(TargetType, (Array)OutList.GetType().GetMethod("ToArray").Invoke(OutList, null)); } OutList.Clear(); OutList = null; }