/// /// Copyright (c) 2013-2019 Sensus Slovensko a.s. /// using System; using System.IO; using System.Collections.Generic; using System.Windows.Forms; using log4net; using Common; using Config.Entities; using Users; using Users.Entities; using TBF.Resources; using TBF.UI.Shared; using NHibernate; namespace TBF { public class Program { public const string HomeDir = "C:\\Tbf\\"; /// Contains subdirectories Results, Logs, Images, ... public const string ConfigDir = "C:\\Tbf\\Cfg\\"; public const string GraphsDir = "C:\\Tbf\\Graphs\\"; public const string ImagesDir = "C:\\Tbf\\Images\\"; public const string TempImagesDir = "C:\\Tbf\\Images\\Temp\\"; public static string ExecutableDir; /// log4net static ILog log; const string log4netConfigFName = "log4netConfig.xml"; /// Program information public static string Version; /// version string public static DateTime BuildDateTime; /// date and time of program build /// Local settings public static LocalSettings LocalSettings; public const string LocalSettingsFileName = "config.xml"; public const string LocalSettingsBackupName = "config.backup.xml"; public static System.Globalization.CultureInfo AltCulture; /// /// Selected procedure data /// - before the main program window is open, objects referenced by this variable /// are loaded from the database. /// - user interface displays data stored in objects referenced by this variable. /// When user modifies procedure settings and confirms changes (choses to save them), /// data referenced by this variable are updated and written to the database. /// - when users starts a test procedure, state machine creates its own copy /// of procedure data and uses them for the test. Changes of 'SelectedProcedure' /// done during the test do not have any influence on the currently running test. /// public static Config.Entities.Procedure SelectedProcedure = null; /// /// Main window object. /// public static UI.MainWnd MainWnd; /// /// The main entry point for the application. /// [STAThread] static void Main() { AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler); /// Program version string System.Reflection.Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly(); ExecutableDir = Path.GetDirectoryName(thisAssembly.Location); Version ver = thisAssembly.GetName().Version; Version = string.Format("{0}.{1}.{2}", ver.Major, ver.Minor, ver.Build); BuildDateTime = new System.IO.FileInfo(thisAssembly.Location).LastWriteTime; /// Local program configuration directory including the trailing backslash string locAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + Path.DirectorySeparatorChar + "TestBenchFramework" + Path.DirectorySeparatorChar; string sampleConfigDir = Application.StartupPath + Path.DirectorySeparatorChar + "SampleConfig" + Path.DirectorySeparatorChar; /// Check whether local application data directory exists. /// If not, create it and copy the initial configuration to this directory. /// This is done only once after a clean program installation on the first program start-up try { if (!Directory.Exists(ConfigDir)) { Directory.CreateDirectory(ConfigDir); Environment.CurrentDirectory = ConfigDir; if (Directory.Exists(locAppDataDir)) { /// /// Copy configuration from locAppDataDir or from SampleConfig if files are missing in locAppDataDir /// if (File.Exists(locAppDataDir + LocalSettingsFileName)) File.Copy(locAppDataDir + LocalSettingsFileName, ConfigDir + LocalSettingsFileName); else if (File.Exists(locAppDataDir + LocalSettingsBackupName)) File.Copy(locAppDataDir + LocalSettingsBackupName, ConfigDir + LocalSettingsFileName); else if (File.Exists(sampleConfigDir + LocalSettingsFileName)) File.Copy(sampleConfigDir + LocalSettingsFileName, ConfigDir + LocalSettingsFileName); if (File.Exists(locAppDataDir + LocalSettingsBackupName)) File.Copy(locAppDataDir + LocalSettingsBackupName, LocalSettingsBackupName); if (File.Exists(locAppDataDir + log4netConfigFName)) File.Copy(locAppDataDir + log4netConfigFName, log4netConfigFName); else if (File.Exists(sampleConfigDir + log4netConfigFName)) File.Copy(sampleConfigDir + log4netConfigFName, log4netConfigFName); File.SetAttributes(LocalSettingsFileName, FileAttributes.Normal); File.SetAttributes(LocalSettingsBackupName, FileAttributes.Normal); File.SetAttributes(log4netConfigFName, FileAttributes.Normal); } else { /// /// Copy configuration from SampleConfig directory /// if (File.Exists(sampleConfigDir + LocalSettingsFileName)) File.Copy(sampleConfigDir + LocalSettingsFileName, LocalSettingsFileName); if (File.Exists(sampleConfigDir + log4netConfigFName)) File.Copy(sampleConfigDir + log4netConfigFName, log4netConfigFName); File.SetAttributes(LocalSettingsFileName, FileAttributes.Normal); File.SetAttributes(log4netConfigFName, FileAttributes.Normal); } } else { Environment.CurrentDirectory = ConfigDir; } if (!Directory.Exists(ImagesDir)) Directory.CreateDirectory(ImagesDir); if (!Directory.Exists(TempImagesDir)) Directory.CreateDirectory(TempImagesDir); IEnumerable files = Directory.EnumerateFiles(TempImagesDir); /// TODO: Implement recursive directory delete foreach (var file in files) File.Delete(file); } catch (Exception e) { MessageBox.Show("Error creating a local application data directory and preparing an initial configuration", "Fatal error"); return; /// Fatal error } /// Configure and start logging log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(ConfigDir + log4netConfigFName)); log = LogManager.GetLogger(typeof(Program)); log.Fatal("--------------------------------------------------------------------------------"); log.Fatal(string.Format("TBF ver.{0}", Version)); log.Debug("List of component classes:"); int count = 0; foreach (var factory in TBF.Rig.TbfComponents.Factories) { log.DebugFormat(" {0:D3} {1}", ++count, factory.ClassName); } /// /// Load the local settings /// Program.LocalSettings = LocalSettings.Load(Program.LocalSettingsFileName); if (Program.LocalSettings == null || Program.LocalSettings.TestBenches == null) { /// Loading local seetings from regular config file failed. Use the backup Program.LocalSettings = LocalSettings.Load(Program.LocalSettingsBackupName); if (Program.LocalSettings == null || Program.LocalSettings.TestBenches == null) { log.Fatal("Application terminated."); MessageBox.Show(string.Format("Could not load file {0}, nor {1}.", Program.LocalSettingsFileName, Program.LocalSettingsBackupName), "Fatal error"); return; /// Fatal error } else { LocalSettings.Save(); /// Save the settings to overwrite the wrong file log.FatalFormat("BatchNr = {0}", LocalSettings.BatchNr); } } else { /// Loading local seetings from the regular config file was successful. Update the backup File.Copy(Program.LocalSettingsFileName, Program.LocalSettingsBackupName, true); log.FatalFormat("BatchNr = {0}", LocalSettings.BatchNr); } try { string culture = LocalSettings.Language.Replace('_', '-'); System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(culture); } catch (Exception e) { string msg = e.Message; MessageBox.Show("Selected language not supported.\nUsing English.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en"); } //AltCulture = null; AltCulture = new System.Globalization.CultureInfo("SK"); /// This belongs to Application.Run(new MainWnd()) ... /// and should be executed before opening 'loginDlg' Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); /// Ensure that there is at least one bench in the configuration while (Program.LocalSettings.BenchesCount == 0) { log.Warn("No test benches in the local configuration -> display an appropriate dialog."); /// Ask what to do, ask for the password, open 'DatabaseSetingsDlg' and continue on OK if ((new NoBenchOrDatabaseDlg { Message = Strings.NoBenchMsg }.ShowDialog() != DialogResult.OK) || (new Users.Forms.LoginDlg(true).ShowDialog() == DialogResult.Cancel) || (new TBF.UI.Settings.BenchesDlg().ShowDialog() == DialogResult.Cancel)) { return; /// Exit program } log.Warn("Loading the local settings again."); Program.LocalSettings = LocalSettings.Load(Program.LocalSettingsFileName); if (Program.LocalSettings == null) return; /// Fatal error } /// User login bool retryLogin = true; /// true = stay in a login loop do { LoginDlgWithBenchSelection loginDlgBench; if (LocalSettings.LastBenchName != null) { loginDlgBench = new LoginDlgWithBenchSelection(LocalSettings.LastBenchName); } else { loginDlgBench = new LoginDlgWithBenchSelection(); } loginDlgBench.Method = LocalSettings.LoginMethod; DialogResult dr = loginDlgBench.ShowDialog(); if (dr == DialogResult.Cancel) { return; /// Login canceled --> Exit application } else { for (int i = 0; i < LocalSettings.BenchesCount; i++) { if (LocalSettings.TestBenches[i].BenchName == loginDlgBench.BenchName) { log.FatalFormat("Test bench name: {0}", loginDlgBench.BenchName); Users.GlobalData.RemoteUsersDB = LocalSettings.TestBenches[i].UsersDBSettings; Users.GlobalData.LocalUsersDB = LocalSettings.TestBenches[i].ProceduresDBSettings; Users.Entities.User loadedUser = null; try { #if TURA_IPERL || TURA_IPERL_NEW || TURA_SPECIAL GID[] reqGrpMembership = new Common.GID[] { Common.GID.Testers, Common.GID.TestingSpecialists, Common.GID.Metrologists, Common.GID.HeadOfLab }; #else GID[] reqGrpMembership = null; #endif bool authorized = false; AuthorizedAs authorizedAs = Common.AuthorizedAs.PowerUser; if (Users.Entities.User.IsPowerUser(loginDlgBench.Alias, loginDlgBench.Password)) { loadedUser = new Users.Entities.User(loginDlgBench.Alias, 6, true); authorized = loadedUser.Authorize(loginDlgBench.Alias, loginDlgBench.Password, reqGrpMembership); if (authorized) { Users.GlobalData.AuthorizedAs = Common.AuthorizedAs.PowerUser; } } /// /// Authorisation using databases /// if (!authorized) { DBSettings[] dbs = new DBSettings[] { Users.GlobalData.RemoteUsersDB, Users.GlobalData.LocalUsersDB }; authorizedAs = Common.AuthorizedAs.RemoteUser; /// Try remote DB first foreach (var db in dbs) { /// Load and authorise user from the UsersDB database try { if (loginDlgBench.Password == LoginDlgWithBenchSelection.UseTheTagPassword) { loadedUser = Users.Entities.User.LoadUserByTag(loginDlgBench.Alias, db); if (loadedUser != null) authorized = loadedUser.AuthorizeTag(loginDlgBench.Alias, reqGrpMembership); } else { switch (loginDlgBench.Method) { default: case Common.LoginMethod.UserName: loadedUser = Users.Entities.User.LoadUserByName(loginDlgBench.Alias, db); if (loadedUser != null) authorized = loadedUser.Authorize(loginDlgBench.Alias, loginDlgBench.Password, reqGrpMembership); break; case Common.LoginMethod.FullName: loadedUser = Users.Entities.User.LoadUserByFullName(loginDlgBench.Alias, db); if (loadedUser != null) authorized = loadedUser.AuthorizeFullName(loginDlgBench.Alias, loginDlgBench.Password, reqGrpMembership); break; case Common.LoginMethod.Number: int number; if (!int.TryParse(loginDlgBench.Alias, out number)) break; loadedUser = Users.Entities.User.LoadUserByNumber(number, db); if (loadedUser != null) authorized = loadedUser.AuthorizeNumber(number, loginDlgBench.Password, reqGrpMembership); break; } } } catch { authorized = false; } if (authorized) break; authorizedAs = Common.AuthorizedAs.LocalUser; /// Try local DB afterwards } } if (authorized) { loadedUser.SetLegalizator(loginDlgBench.Legalizator); Users.GlobalData.AuthorizedAs = authorizedAs; LocalSettings.LoginMethod = loginDlgBench.Method; /// Save the login method actually used if (LocalSettings.TestBenches[i].IsRealBench) { /// This is a real bench ==> check if another instance is running string processName = System.IO.Path.GetFileNameWithoutExtension(thisAssembly.Location); if (System.Diagnostics.Process.GetProcessesByName(processName).Length > 1) { MessageBox.Show(Strings.Another_instance_is_running, Strings.Error, MessageBoxButtons.OK, MessageBoxIcon.Asterisk); return; } } log.InfoFormat("Password accepted, bench '{0}' parameters loaded.", LocalSettings.TestBenches[i].BenchName); /// Copy the selected bench settings to CurrentBench. /// Clone() guarantees that current bench settings wont be modified /// when user modifies the database settings in DatabaseSettingsDlg. TBF.DB.CurrentBench = (DatabaseSettings)LocalSettings.TestBenches[i].Clone(); Results.DB.DbType = TBF.DB.CurrentBench.WaterMetersDBSettings.DbType; Results.DB.ConnectionString = TBF.DB.CurrentBench.WaterMetersDBSettings.ConnectionString; /// Save the selection to local settings LocalSettings.LastBenchName = TBF.DB.CurrentBench.BenchName; /// /// Connect to Config database. /// This triggers an exception in case user is a power user and there is no Config database. /// IList listOfProcedures = TBF.DB.CreateSession(Common.DBKind.Config) .QueryOver() .Where(x => (x.ProcedureState == Common.ProcedureState.Active)) .And(x => (x.Name == LocalSettings.LastProcedureName)) .List(); /// /// Connect to Results database and determine the last saved batch number. /// Results.DB.LoadSharedData(); int maxBatchNr = Results.DB.GetMaxSavedBatchNr(); if (!TBF.DB.CurrentBench.IsRealBench || Program.LocalSettings.BatchNr <= maxBatchNr) { log.FatalFormat("Max. BatchNr in DB = {0}, LocalSettings.BatchNr = {1}", maxBatchNr, Program.LocalSettings.BatchNr); Program.LocalSettings.BatchNr = maxBatchNr + 1; Program.LocalSettings.Save(); log.FatalFormat("LocalSettings.BatchNr updated to {0}", Program.LocalSettings.BatchNr); } /// /// Connect to Events database (if any) and load recent events from this test bench. /// if (TBF.DB.CurrentBench.EventsDBSettings.DbType != Common.DBType.None && !string.IsNullOrEmpty(TBF.DB.CurrentBench.EventsDBSettings.ConnectionString)) { Events.DB.DbType = (DBType)TBF.DB.CurrentBench.EventsDBSettings.DbType; Events.DB.ConnectionString = TBF.DB.CurrentBench.EventsDBSettings.ConnectionString; Events.DB.LoadRecentEvents(Events.DB.CreateSession(), loginDlgBench.BenchName, 7); /// Last 7 days } retryLogin = false; break; } if (retryLogin) { /// Close Fluent NHibernate MessageBox.Show(Strings.Invalid_user_password_or_bench, Strings.Error, MessageBoxButtons.OK); } } catch (Exception exc) { /// Database connect failed, create a log log.FatalFormat("Connection to database failed: {0}", exc.Message); if (exc.InnerException != null && !string.IsNullOrEmpty(exc.InnerException.Message)) { log.FatalFormat("Inner exception: {0}", exc.InnerException.Message); } /// Ask what to do switch ((new NoBenchOrDatabaseDlg { Message = Strings.NoDatabaseMsg, ShowRetry = true }).ShowDialog()) { case DialogResult.Abort: return; /// Exit program case DialogResult.Retry: break; /// Retry connection to the database, stay inside the loop case DialogResult.Yes: new TBF.UI.Settings.UpgradeSelectionDlg().ShowDialog(); break; /// Stay inside the loop default: /// Open 'DatabaseSetingsDlg' and retry DB connect on OK if (new Users.Forms.LoginDlg(true).ShowDialog() == DialogResult.Cancel || new TBF.UI.Settings.BenchesDlg().ShowDialog() == DialogResult.Cancel) { return; /// Exit program } if (Program.LocalSettings == null) return; /// Fatal error -> Exit program else break; /// Stay inside the loop } } } // if benchname is OK } // for all benches in local settings loop } // not Canceled } //do while (retryLogin); /// /// Load the last procedure or create a new, default one /// if (LocalSettings.LastProcedureName != null) { IList listOfProcedures = TBF.DB.CreateSession(Common.DBKind.Config) .QueryOver() .Where(x => (x.ProcedureState == Common.ProcedureState.Active)) .And(x => (x.Name == LocalSettings.LastProcedureName)) .List(); if (listOfProcedures.Count == 1) { log.InfoFormat("Pre-selecting the procedure {0}", LocalSettings.LastProcedureName); SelectedProcedure = listOfProcedures[0]; } } try { /// Open the main application window log.Info("Creating the main window"); MainWnd = new UI.MainWnd(); log.Info("Opening the main window"); Application.Run(MainWnd); log.Info("The main window was closed"); } catch (Exception e) { LogException(log, "Exception in Application.Run(MainWnd)", e); } finally { /// Save local settings LocalSettings.Save(); } /// Close Fluent NHibernate /// Todo log.Info("Exiting application."); } static void MyHandler(object sender, UnhandledExceptionEventArgs args) { LogException(log, "Unhandled exception", (Exception)args.ExceptionObject); } static void LogException(ILog log, string description, Exception e) { log.FatalFormat("---------------( {0} )---------------", description); log.FatalFormat("Message : {0}", e.Message); if (e.InnerException != null) { log.FatalFormat("InnerMessage : {0}", e.InnerException.Message); } log.FatalFormat("StackTrace : {0}{1}", Environment.NewLine, e.StackTrace); log.Fatal("--------------------------------------"); } } }