tbf/TBF/Program.cs
Milan Hanajik a70491d236 Clean-up
2022-06-01 12:09:31 +02:00

505 lines
25 KiB
C#

///
/// 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;
/// <summary>
/// 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.
/// </summary>
public static Config.Entities.Procedure SelectedProcedure = null;
/// <summary>
/// Main window object.
/// </summary>
public static UI.MainWnd MainWnd;
/// <summary>
/// The main entry point for the application.
/// </summary>
[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<string> 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));
///
/// 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.CurrentUser.RemoteUsersDB = LocalSettings.TestBenches[i].UsersDBSettings;
Users.CurrentUser.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, null);
if (authorized)
{
Users.CurrentUser.AuthorizedAs = Common.AuthorizedAs.PowerUser;
}
}
///
/// Authorisation using databases
///
if (!authorized)
{
DBSettings[] dbs = new DBSettings[] { Users.CurrentUser.RemoteUsersDB, Users.CurrentUser.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, null);
}
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, null);
break;
case Common.LoginMethod.FullName:
loadedUser = Users.Entities.User.LoadUserByFullName(loginDlgBench.Alias, db);
if (loadedUser != null) authorized = loadedUser.AuthorizeFullName(loginDlgBench.Alias, loginDlgBench.Password, reqGrpMembership, null);
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, null);
break;
}
}
}
catch
{
authorized = false;
}
if (authorized) break;
authorizedAs = Common.AuthorizedAs.LocalUser; /// Try local DB afterwards
}
}
if (authorized)
{
loadedUser.SetLegalizator(loginDlgBench.Legalizator);
Users.CurrentUser.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<Procedure> listOfProcedures = TBF.DB.CreateSession(Common.DBKind.Config)
.QueryOver<Procedure>()
.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<Procedure> listOfProcedures = TBF.DB.CreateSession(Common.DBKind.Config)
.QueryOver<Procedure>()
.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("--------------------------------------");
}
}
}