using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using POSV.Entity;
using POSV.HttpApi;
using POSV.ShoppingCart;
using POSV.Utils;
using System.Data.SQLite;
using POSV.Cef.Client.ClientComm;
using POS.Language.Language;
using POSV.OtherWaiMai;
using POSV.QiMai;
using POSV.Common.Util;
using NLog.RestTarget;
using POSV.Common.Wlan;
using System.Net.NetworkInformation;
using POSV.Payment.Abmcs;
using System.Security.Cryptography;
using POSV.Dianping;
using POSV.Card;
namespace POSV
{
static class Program
{
private const int SW_HIDE = 0;
private const int SW_NORMAL = 1;
private const int SW_MAXIMIZE = 3;
private const int SW_SHOWNOACTIVATE = 4;
private const int SW_SHOW = 5;
private const int SW_MINIMIZE = 6;
private const int SW_RESTORE = 9;
private const int SW_SHOWDEFAULT = 10;
private static ExceptionForm exception = null;
/// 该函数设置由不同线程产生的窗口的显示状态
///
/// 窗口句柄
/// 指定窗口如何显示。查看允许值列表,请查阅ShowWlndow函数的说明部分
/// 如果函数原来可见,返回值为非零;如果函数原来被隐藏,返回值为零
[DllImport("User32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
///
/// 该函数将创建指定窗口的线程设置到前台,并且激活该窗口。键盘输入转向该窗口,并为用户改各种可视的记号。
/// 系统给创建前台窗口的线程分配的权限稍高于其他线程。
///
/// 将被激活并被调入前台的窗口句柄
/// 如果窗口设入了前台,返回值为非零;如果窗口未被设入前台,返回值为零
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public static Mutex AppMutex;
///
/// 应用程序的主入口点。
///
[STAThread]
static void Main()
{
//全局异常捕捉
Application.ThreadException += ApplicationThreadException; //UI线程异常
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException; //多线程异常
//NetworkInterface[] allInterfaces = NetworkInterface.GetAllNetworkInterfaces();
//NetworkInterface firstInterface = allInterfaces[0];
//foreach (var i in allInterfaces)
//{
// bool interfaceSupportsIPv6 = InterfaceHasIpv6Enabled(i);
//}
bool createdNew = true;
AppMutex = new System.Threading.Mutex(true, Application.ProductName, out createdNew);
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashScreen.ShowSplashScreen();
//初始化NLog
NLogUtils.InitLogger();
SplashScreen.SetStatus("加载运行日志...");
Global.Instance.Restart = false;
AutoUpdateNew();
var _type = Global.Instance.GlobalConfigStringValue(ConfigConstant.LAYOUT_LANGUAGETYPE, "中文");
LangProxy.SetLangTypeVal(_type);
//检测是否WiFi网路连接
Global.Instance.IsWiFi = WiFiApi.Connected();
SplashScreen.SetStatus(LangProxy.ToLang("检测本机网络连接..."));
var openApiTask = Task.Factory.StartNew(() => { return HttpClientUtils.IsAvailable(); });
Task.WaitAll(openApiTask);
//网络可用:a)网卡工作;b)可以联网;c)开放平台正常
bool isConnected = openApiTask.Result;
//网络连接压入全局参数
Global.Instance.Online = isConnected;
SplashScreen.SetStatus(string.Format(LangProxy.ToLang("本机网络连接{0}..."), isConnected ? LangProxy.ToLang("正常") : LangProxy.ToLang("不正常")));
SplashScreen.SetStatus(LangProxy.ToLang("检测服务中心连接..."));
try
{
Global.Instance.MessageCenterOnline = MessageCenterUtils.Instance.IsAvailable();
}
catch { }
SplashScreen.SetStatus(LangProxy.ToLang("设备自检中..."));
var finger = DeviceUtils.Instance;
bool isGo = true;
SplashScreen.SetStatus(LangProxy.ToLang("检测数据库版本..."));
bool isException = AutoUpdateDatabase();
if (isException)
{
isGo = false;
SplashScreen.SetStatus(LangProxy.ToLang("数据更新失败,即将退出..."));
}
else
{
SplashScreen.SetStatus(LangProxy.ToLang("数据更新成功..."));
}
if (isGo)
{
SplashScreen.SetStatus(LangProxy.ToLang("启动定时任务..."));
JobUtils.Startup();
SplashScreen.CloseForm();
//初始化来电猫全局参数,应用关闭后主动关闭端口
Global.Instance.Cats = new List();
//登陆窗口,并最大化
FastLoginForm loginForm = new FastLoginForm();
ShowWindowAsync(loginForm.Handle, SW_MAXIMIZE);//显示
SetForegroundWindow(loginForm.Handle);//当到最前端
if (DialogResult.OK == loginForm.ShowDialog())
{
//zhangy 2020-03-05 Add 登陆成功后,更新数据库默认参数
//AutoUpdateParamVersion();
//联机状态
if (Global.Instance.Online)
{
var download = new FastDownloading();
if (DialogResult.OK == download.ShowDialog())
{
var wsse = WSSE.Instance;
StartTheme();
}
}
else
{
var wsse = WSSE.Instance;
StartTheme();
}
}
//关闭来电猫端口
Global.Instance.Cats.ForEach(x =>
{
if (x != null && x.IsOpen)
{
x.Close();
NLog.LogManager.GetCurrentClassLogger().Info("关闭来电参数<{0},{1}>", x.PortName, x.BaudRate);
}
});
//关闭云端推送连接
Proxy.Mqtt.MqttClientUtils.Instance.Stop();
NLog.LogManager.GetCurrentClassLogger().Info("关闭云端推送连接");
//关闭消息中心连接
MqttUtils.Instance.Stop();
NLog.LogManager.GetCurrentClassLogger().Info("关闭消息中心连接");
//关闭计划任务
JobUtils.Stop();
NLog.LogManager.GetCurrentClassLogger().Info("关闭计划任务");
if (Global.Instance.Restart)
{
AppMutex.ReleaseMutex();
AppMutex.Dispose();
AppMutex = null;
Application.Exit();
System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().Location);
System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
currentProcess.Kill();
}
else
{
System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
currentProcess.Kill();
}
}
else
{
Thread.Sleep(3000);
SplashScreen.CloseForm();
Application.ExitThread();
}
}
else
{
Process instance = Instance();
ShowWindowAsync(instance.MainWindowHandle, SW_MAXIMIZE);//显示
SetForegroundWindow(instance.MainWindowHandle);//当到最前端
}
}
///
/// 启动对应主题布局界面
///
private static void StartTheme()
{
Global.Instance.ReloadConfig();
try
{
//版本选择
ThemeEnum enableTheme = ThemeEnum.快餐;
var layoutTheme = Global.Instance.GlobalConfigStringValue(ConfigConstant.LAYOUT_THEME, ThemeEnum.快餐.ToString());
Enum.TryParse(layoutTheme, out enableTheme);
switch (enableTheme)
{
case ThemeEnum.茶饮:
{
Application.Run(new TeaTemplate());
}
break;
case ThemeEnum.新食堂:
{
DownloadCefDependentFile DownloadCef = new DownloadCefDependentFile();
if (!DownloadCef.Needtodownload())
{
DownloadCef.ShowDialog();
if (!DownloadCef.IsAccomplish)
{
//zhangy 2020-03-01 Add 新食堂版本依赖CEF环境,如果环境异常,重置到快餐模式,以便于下次正常启动
using (var db = Global.Instance.OpenDataBase)
{
var config = db.Single(" where [group] = @0 and keys = @1", ConfigConstant.LAYOUT_GROUP, ConfigConstant.LAYOUT_THEME);
if (config != null)
{
config.Values = ThemeEnum.快餐.ToString();
using (var trans = db.GetTransaction())
{
db.Save(config);
trans.Complete();
}
}
}
MessageBox.Show("运行环境出现问题,已经恢复到快餐模式,请与管理员联系!");
Application.ExitThread();
}
}
Application.Run(new POSV.Cef.FastTemplate.FastTemplate());
}
break;
case ThemeEnum.食堂:
{
Application.Run(new FastTemplate());//zhangy 2020-002-18 Add 已经在参数配置中屏蔽
}
break;
case ThemeEnum.称重:
{
Application.Run(new WeightTemplate());
}
break;
case ThemeEnum.快餐:
default:
{
Application.Run(new DefaultTemplate());
}
break;
}
}
catch (Exception ex)
{
if (exception == null || exception.IsDisposed)
{
exception = new ExceptionForm();
}
exception.SetMessage = ex.Message;
exception.SetErrorMessage = ex.StackTrace;
exception.ShowDialog();
}
}
///
/// 只运行一个实例
///
///
private static Process Instance()
{
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(current.ProcessName);
foreach (Process process in processes)
{
//如果实例已经存在则忽略当前进程
if (process.Id != current.Id)
{
//保证要打开的进程同已经存在的进程来自同一文件路径
if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName)
{
//返回已经存在的进程
return process;
}
}
}
return null;
}
///
/// 主程序更新 AutoUpdater.exe
///
static void AutoUpdateNew()
{
string auto_update = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"AutoUpdater.exe");
string auto_update_new = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Constant.AUTO_UPDATER_NEW);
string auto_update_backup = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\backup\AutoUpdater.exe");
if (File.Exists(auto_update_new))
{
if (File.Exists(auto_update))
{
if (File.Exists(auto_update_backup))
{
File.Delete(auto_update_backup);
}
File.Move(auto_update, auto_update_backup);
}
FileInfo fi = new FileInfo(auto_update_new);
fi.MoveTo(auto_update);
}
}
//static void AutoUpdateParamVersion()
//{
// try
// {
// ApplicationVersion entity = null;
// //判断数据库中记录的版本
// using (var db = Global.Instance.OpenDataBase)
// {
// entity = db.Query().OrderByDescending(x => x.ModifyDate).FirstOrDefault();
// }
// if(entity != null)
// {
// //数据库版本大于参数版本
// if (string.Compare(entity.DbVersion,entity.ParamVersion) > 0)
// {
// var _name = string.Format("data.db_{0}.txt", entity.DbVersion);
// //获得正在运行类所在的名称空间
// Type type = MethodBase.GetCurrentMethod().DeclaringType;
// string _namespace = type.Namespace;
// //获得当前运行的Assembly
// Assembly _assembly = Assembly.GetExecutingAssembly();
// //根据名称空间和文件名生成资源名称
// string resourceName = _namespace + "." + _name;
// var list = new ConcurrentQueue();
// using (var stream = _assembly.GetManifestResourceStream(resourceName))
// {
// if (stream != null)
// {
// NLog.LogManager.GetCurrentClassLogger().Info("默认参数更新<{0}>", _name);
// string json = string.Empty;
// using (StreamReader reader = new StreamReader(stream))
// {
// json = reader.ReadToEnd();
// }
// if (!string.IsNullOrEmpty(json))
// {
// var migrate = DynamicJson.Parse(json);
// if (migrate != null)
// {
// //处理租户更新,影响具体租户全部门店
// if (migrate.IsDefined("tenant") && Global.Instance.Authc != null)
// {
// var tenant = migrate["tenant"];
// //zhangy 2020-03-05 JSON解析有问题,不能以数字开头
// var tenantNo = "tenant_" + Global.Instance.Authc.TenantId;
// if (tenant.IsDefined(tenantNo))
// {
// var arrayJson = tenant[tenantNo];
// foreach (var item in arrayJson)
// {
// list.Enqueue(item);
// }
// }
// }
// //处理租户门店更新,影响具体租户具体门店
// if (migrate.IsDefined("store") && Global.Instance.Authc != null)
// {
// var storeNo = "store" + Global.Instance.Authc.TenantId + "_" + Global.Instance.Authc.StoreNo;
// var tenant = migrate["store"];
// if (tenant.IsDefined(storeNo))
// {
// var arrayJson = tenant[storeNo];
// foreach (var item in arrayJson)
// {
// list.Enqueue(item);
// }
// }
// }
// }
// }
// }
// }
// if(list.Count > 0)
// {
// SqliteUtils.ExecuteTransaction(list);
// //刷新参数
// Global.Instance.ReloadConfig();
// NLog.LogManager.GetCurrentClassLogger().Info("门店默认参数更新成功");
// }
// entity.ParamVersion = entity.DbVersion;
// using (var db = Global.Instance.OpenDataBase)
// {
// using (var trans = db.GetTransaction())
// {
// db.Save(entity);
// trans.Complete();
// }
// }
// }
// }
// else
// {
// NLog.LogManager.GetCurrentClassLogger().Info("门店默认参数更新失败:原因:表没有数据");
// }
// }
// catch (Exception ex)
// {
// NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级异常");
// }
//}
static bool AutoUpdateDatabase()
{
bool isException = false;
//当前程序嵌入的数据库版本
int _version = 1;
//数据库的版本
int _dbVersion = 1;
ApplicationVersion entity = null;
try
{
var _array = Application.ProductVersion.Split('.');
int major = 0;
int.TryParse(_array[0], out major);
int minor = 0;
int.TryParse(_array[1], out minor);
int build = 2;
int.TryParse(_array[2], out build);
//当前程序嵌入的数据库版本
_version = major * 100 + minor * 10 + build;
//应用的版本
var _appVersion = Application.ProductVersion;
//判断数据库中记录的版本
using (var db = Global.Instance.OpenDataBase)
{
entity = db.Query().OrderByDescending(x => x.ModifyDate).FirstOrDefault();
}
if (entity == null)
{
entity = new ApplicationVersion();
entity.Id = IdWorkerUtils.Instance.NextId();
entity.AppVersion = _appVersion;
entity.DbVersion = _dbVersion.ToString();
}
int.TryParse(entity.DbVersion, out _dbVersion);
//升级前备份数据文件
string sourceFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\posv2.s3db");
string destFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format(@"data\backup\posv2.s3db", ""));
//数据库需要升级
if (_version > _dbVersion)
{
SplashScreen.SetStatus(string.Format("备份历史数据<{0}>...", _dbVersion));
System.IO.File.Copy(sourceFileName, destFileName, true);
for (int i = _dbVersion + 1; i <= _version; i++)
{
var _name = string.Format("data.db_{0}.txt", i);
//获得正在运行类所在的名称空间
Type type = MethodBase.GetCurrentMethod().DeclaringType;
string _namespace = type.Namespace;
//获得当前运行的Assembly
Assembly _assembly = Assembly.GetExecutingAssembly();
//根据名称空间和文件名生成资源名称
string resourceName = _namespace + "." + _name;
Tuple> migrate = null;
using (Stream stream = _assembly.GetManifestResourceStream(resourceName))
{
if (stream == null)
{
continue;
}
NLog.LogManager.GetCurrentClassLogger().Info("发布数据库更新<{0}>", _name);
string json = string.Empty;
using (StreamReader reader = new StreamReader(stream))
{
json = reader.ReadToEnd();
}
if (!string.IsNullOrEmpty(json))
{
migrate = ParseDbMigrate(json);
}
}
if (migrate != null && migrate.Item1)
{
var sqls = migrate.Item2;
SplashScreen.SetStatus(string.Format("更新数据,请稍候..."));
foreach (var sql in migrate.Item2)
{
try
{
SqliteUtils.Execute(sql);
}
catch (Exception ex)
{
if (ex.Message.Contains("duplicate column name"))
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, "列名升级失败,已存在:"+ sql);
}
else
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级异常");
}
}
}
}
}
}
}
catch (Exception ex)
{
isException = true;
NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级异常");
}
finally
{
if (!isException)
{
entity.AppVersion = Application.ProductVersion;
entity.DbVersion = _version.ToString();
using (var db = Global.Instance.OpenDataBase)
{
using (var trans = db.GetTransaction())
{
db.Save(entity);
trans.Complete();
}
}
}
//刷新Http请求的AppKey,避免调用错误
OpenApiUtils.Instance.Refresh();
}
return isException;
}
static Tuple> ParseDbMigrate(string json)
{
Tuple> result = null;
try
{
NLog.LogManager.GetCurrentClassLogger().Info("解析数据库更新脚本......");
var list = new ConcurrentQueue();
var migrate = DynamicJson.Parse(json);
if (migrate != null)
{
//处理全局更新,影响全部客户
if (migrate.IsDefined("global"))
{
var global = migrate["global"];
if (global.IsDefined("all"))
{
var arrayJson = global["all"];
foreach (var item in arrayJson)
{
list.Enqueue(item);
}
NLog.LogManager.GetCurrentClassLogger().Info("处理数据库全局更新......");
}
}
////处理租户更新,影响具体租户全部门店
//if (migrate.IsDefined("tenant") && Global.Instance.Authc != null)
//{
// var tenant = migrate["tenant"];
// var tenantNo = Global.Instance.Authc.TenantId;
// if (tenant.IsDefined(tenantNo))
// {
// var arrayJson = tenant[tenantNo];
// foreach (var item in arrayJson)
// {
// list.Enqueue(item);
// }
// NLog.LogManager.GetCurrentClassLogger().Info("处理数据库租户更新,受影响的记录:<{0}>", arrayJson != null ? arrayJson.Length : 0);
// }
//}
////处理租户门店更新,影响具体租户具体门店
//if (migrate.IsDefined("store") && Global.Instance.Authc != null)
//{
// var storeNo = Global.Instance.Authc.TenantId+"_"+ Global.Instance.Authc.StoreNo;
// var tenant = migrate["store"];
// if (tenant.IsDefined(storeNo))
// {
// var arrayJson = tenant[storeNo];
// foreach (var item in arrayJson)
// {
// list.Enqueue(item);
// }
// NLog.LogManager.GetCurrentClassLogger().Info("处理数据库租户更新,受影响的记录:<{0}>", arrayJson != null ? arrayJson.Length : 0);
// }
//}
}
result = new Tuple>(true, list);
}
catch (Exception ex)
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级文件格式错误");
result = new Tuple>(false, null);
}
return result;
}
///
/// 修改数据库密码
///
public static void ChangePwd(string pwd)
{
SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder();
builder.DataSource = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\posv2.s3db");
builder.Password = "passwd";
builder.Version = 3;
builder.CacheSize = 10000;
builder.DefaultTimeout = 5000;
builder.PageSize = 4096;
builder.FailIfMissing = true;
builder.Pooling = true;
builder.DefaultIsolationLevel = System.Data.IsolationLevel.ReadCommitted;
builder.SyncMode = SynchronizationModes.Off;
builder.JournalMode = SQLiteJournalModeEnum.Truncate;
builder.UseUTF16Encoding = false;
using (SQLiteConnection connection = new SQLiteConnection(builder.ToString()))
{
try
{
connection.Open();
connection.ChangePassword(pwd);
}
catch (SQLiteException ex)
{
throw new Exception(ex.Message);
}
finally
{
connection.Close();
}
}
}
//UI线程异常
static void ApplicationThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
NLog.LogManager.GetCurrentClassLogger().Error(e.Exception, "ApplicationThreadException");
Global.Instance.BugReport(e.Exception);
}
//多线程异常
static void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
NLog.LogManager.GetCurrentClassLogger().Error(e.ExceptionObject as Exception, "ApplicationThreadException");
Global.Instance.BugReport(e.ExceptionObject as Exception);
}
public static bool InterfaceHasIpv6Enabled(NetworkInterface @interface)
{
try
{
var properties = @interface.GetIPProperties().GetIPv6Properties();
return properties.Index > -999;
}
catch (NetworkInformationException)
{
return false;
}
catch (Exception ex)
{
throw ex;
}
}
}
}