using BellwetherBackend.Utility; using SocketTransfer; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.IO; using System.Windows.Threading; using ICSharpCode.SharpZipLib.Zip; using System.Threading; using System.Net; namespace BellwetherBackend.SubPage { public enum UpdateState { TransferCompleted, Transferring, Disconnected, UpdateCompleted, UpdateFail, Abort, Connected } /// /// Interaction logic for UpdatePage.xaml /// public partial class UpdatePage : UserControl { private static SolidColorBrush IsConnectBrush = new SolidColorBrush(Colors.Green); private static SolidColorBrush IsDisconnectBrush = new SolidColorBrush(Colors.Red); private static string rootPath = System.AppDomain.CurrentDomain.BaseDirectory; private static string tempFolder = @"Temp\"; private static string defaultZipFileName = @"Temp\Data.zip"; private string ipAddress; public string IpAddress { get { if (CheckIpFormat(uxIp1Text.Text) && CheckIpFormat(uxIp2Text.Text) && CheckIpFormat(uxIp3Text.Text) && CheckIpFormat(uxIp4Text.Text)) { ipAddress = String.Format("{0}.{1}.{2}.{3}", uxIp1Text.Text, uxIp2Text.Text, uxIp3Text.Text, uxIp4Text.Text); } else { ipAddress = "error format"; } return ipAddress; } set { ipAddress = value; string[] str = ipAddress.Split('.'); if (str.Length > 3) { for (int i = 0; i < ipTextBlock.Length; i++) { ipTextBlock[i].Text = str[i]; } } this.Dispatcher.BeginInvoke((Action)delegate () { CheckKioskConnect(); }); } } private int ipPort; public int IpPort { set { ipPort = value; } get { return ipPort; } } private bool isConnect; public bool IsConnect { get { return isConnect; } set { isConnect = value; if (isConnect) { uxConnectStateRect.Fill = IsConnectBrush; uxConnectBtn.IsEnabled = false; uxDisConnectBtn.IsEnabled = true; uxIpGrid.IsEnabled = false; uxUpdateBtn.IsEnabled = true; UpdatingState = UpdateState.Connected; } else { uxConnectStateRect.Fill = IsDisconnectBrush; uxConnectBtn.IsEnabled = true; uxDisConnectBtn.IsEnabled = false; uxIpGrid.IsEnabled = true; uxUpdateBtn.IsEnabled = false; UpdatingState = UpdateState.Disconnected; } } } private UpdateState updatingState; public UpdateState UpdatingState { get { return updatingState; } set { updatingState = value; } } private string localipAddress; private TextBox[] ipTextBlock; private BackgroundWorker connectWorker; private SocketClient KioskSocketClient; private bool canTransFileTag = false; private List wholeDataFolder = new List(); private List cutFilePaths = new List(); public UpdatePage() { InitializeComponent(); CreateTempFolder(); new GlobalFunction(); uxConnectBtn.IconData = GlobalFunction.ConnectBtnData; uxDisConnectBtn.IconData = GlobalFunction.DisconnectBtnData; uxUpdateBtn.IconData = GlobalFunction.UpdateBtnData; localipAddress = GetLocalIpAddress(); // 連線 ipTextBlock = new TextBox[] { uxIp1Text, uxIp2Text, uxIp3Text, uxIp4Text }; IpAddress = Properties.Settings.Default["ConnectIP"].ToString(); IpPort = int.Parse(Properties.Settings.Default["ConnectPort"].ToString()); connectWorker = new BackgroundWorker(); connectWorker.WorkerSupportsCancellation = true; connectWorker.DoWork += connectWorker_DoWork; connectWorker.RunWorkerCompleted += connectWorker_RunWorkerCompleted; uxIp1Text.TextChanged += uxIp1Text_TextChanged; uxIp2Text.TextChanged += uxIp1Text_TextChanged; uxIp3Text.TextChanged += uxIp1Text_TextChanged; uxIp4Text.TextChanged += uxIp1Text_TextChanged; this.Loaded += UpdatePage_Loaded; } private void UpdatePage_Loaded(object sender, RoutedEventArgs e) { //Setting.PageNameList[x] List checkBoxList = new List(); foreach (UIElement element in uxUpdateListPanel.Children) { if (element is CheckBox checkBox) checkBoxList.Add(checkBox); } //var visualElementCnt = checkBoxList.Count; //var dataStringCnt = Setting.PageNameList.Count; //for (int pageIndex = 0; pageIndex + 1 < visualElementCnt && pageIndex < dataStringCnt; pageIndex++) //{ // checkBoxList[pageIndex + 1].Content = Setting.PageNameList[pageIndex]; //} } public void CheckKioskConnect() { if (connectWorker.IsBusy) { connectWorker.CancelAsync(); } connectWorker.RunWorkerAsync(); } public void Disconnect() { DisconnectHandle(); } private void StartLoading() { uxProgressBar.Visibility = Visibility.Visible; uxWorkingGrid.IsEnabled = false; uxWorkingGrid.Opacity = 0.3; } private void StopLoading() { uxProgressBar.Visibility = Visibility.Collapsed; uxWorkingGrid.IsEnabled = true; uxWorkingGrid.Opacity = 1.0; } private void CreateTempFolder() { string path = System.IO.Path.Combine(rootPath, tempFolder); if (Directory.Exists(path)) { ClearTempFolder(); } else { Directory.CreateDirectory(path); } } private void ClearTempFolder() { string path = System.IO.Path.Combine(rootPath, tempFolder); if (Directory.Exists(path)) { string[] files = Directory.GetFiles(path); foreach (string file in files) { try { if (File.Exists(file)) File.Delete(file); } catch { } } } } private string GetLocalIpAddress() { string result = ""; string strHostName = Dns.GetHostName(); IPHostEntry iphostentry = Dns.GetHostEntry(strHostName); foreach (IPAddress ipaddress in iphostentry.AddressList) { // 只取得IP V4的Address if (ipaddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { result = ipaddress.ToString(); } } return result; } private bool CheckIpFormat(string str) { byte checkPart = 0; if (!byte.TryParse(str, out checkPart)) { return false; } return true; } private void InitialUpdateStatus() { this.Dispatcher.BeginInvoke((Action)delegate () { uxStatus.Text = ""; uxUpdateProgressBar.Value = 0; if (KioskSocketClient != null) uxUpdateBtn.IsEnabled = true; ClearTempFolder(); }, DispatcherPriority.Background); } private void ChangeUpdateProgrssBarValue(double value) { this.Dispatcher.BeginInvoke((Action)delegate () { uxUpdateProgressBar.Value += value; }, DispatcherPriority.Background); } private void uxIp1Text_TextChanged(object sender, TextChangedEventArgs e) { Properties.Settings.Default["ConnectIP"] = uxIp1Text.Text + "." + uxIp2Text.Text + "." + uxIp3Text.Text + "." + uxIp4Text.Text; } private void connectWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.Dispatcher.BeginInvoke((Action)delegate () { StopLoading(); }, DispatcherPriority.Background); } private void connectWorker_DoWork(object sender, DoWorkEventArgs e) { this.Dispatcher.BeginInvoke((Action)delegate () { // loading bar StartLoading(); }, DispatcherPriority.Background); if (KioskSocketClient != null) { KioskSocketClient.ServerReady -= KioskSocketClient_ServerReady; KioskSocketClient.ServerClose -= KioskSocketClient_ServerClose; } string serverIp = String.Empty; this.Dispatcher.Invoke((Action)delegate () { serverIp = IpAddress; if (serverIp != "error format") { KioskSocketClient = new SocketClient(IpAddress, IpPort, localipAddress); KioskSocketClient.ServerReady += KioskSocketClient_ServerReady; KioskSocketClient.ServerClose += KioskSocketClient_ServerClose; } }, DispatcherPriority.Background); if (serverIp != "error format") { try { KioskSocketClient.Connect(); } catch { this.Dispatcher.BeginInvoke((Action)delegate () { IsConnect = false; }, DispatcherPriority.Background); } } else { this.Dispatcher.BeginInvoke((Action)delegate () { IsConnect = false; }, DispatcherPriority.Background); } } private void KioskSocketClient_ServerReady(string serverIP, string version) { this.Dispatcher.BeginInvoke((Action)delegate () { IsConnect = true; }, DispatcherPriority.Background); } private void KioskSocketClient_ServerClose(string serverIP) { this.Dispatcher.BeginInvoke((Action)delegate () { IsConnect = false; }, DispatcherPriority.Background); } private void uxConnect_Click(object sender, RoutedEventArgs e) { CheckKioskConnect(); } private void uxDisConnectBtn_Click(object sender, RoutedEventArgs e) { DisconnectHandle(); } private void DisconnectHandle() { if (KioskSocketClient != null) { KioskSocketClient.ServerReady -= KioskSocketClient_ServerReady; KioskSocketClient.ServerClose -= KioskSocketClient_ServerClose; KioskSocketClient.Close(); KioskSocketClient = null; } IsConnect = false; UpdatingState = UpdateState.Abort; } #region 更新開始 private void uxUpdateBtn_Click(object sender, RoutedEventArgs e) { List packageFilePath = new List(); canTransFileTag = true; uxUpdateBtn.IsEnabled = false; // 收集欲打包的資料路徑 StatusChange("收集打包資訊.."); wholeDataFolder = GetPackageFolder(); packageFilePath = GetUpdatePackageFilePath(); if (wholeDataFolder.Count == 0) { StatusChange("沒有選擇任何傳送檔案.."); // 所有狀態回復 WpfDelayDoWork.DoBackgroundWork(InitialUpdateStatus, 3000); return; } // Step 1 : 打包 uxUpdateProgressBar.Value = 0; StatusChange("開始打包.."); BackgroundWorker zipWorker = new BackgroundWorker(); zipWorker.WorkerSupportsCancellation = true; zipWorker.DoWork += zipWorker_DoWork; zipWorker.RunWorkerCompleted += zipWorker_RunWorkerCompleted; zipWorker.RunWorkerAsync(packageFilePath); } private void StatusChange(string status) { this.Dispatcher.BeginInvoke((Action)delegate () { uxStatus.Text = status; }, DispatcherPriority.Background); } private void DivisionZipFile() { // Step 2 : 分割 uxUpdateProgressBar.Value = 0; StatusChange("分割封裝檔.."); BackgroundWorker divisionWorker = new BackgroundWorker(); divisionWorker.WorkerSupportsCancellation = true; divisionWorker.DoWork += divisionWorker_DoWork; divisionWorker.RunWorkerCompleted += divisionWorker_RunWorkerCompleted; divisionWorker.RunWorkerAsync(); } private void TransCutFile() { // Step 3 : 傳送 uxUpdateProgressBar.Value = 0; StatusChange("檔案傳送中..."); BackgroundWorker updateWorker = new BackgroundWorker(); updateWorker.WorkerSupportsCancellation = true; updateWorker.DoWork += updateWorker_DoWork; updateWorker.RunWorkerCompleted += updateWorker_RunWorkerCompleted; updateWorker.RunWorkerAsync(); } #endregion #region 收集欲打包的資料 private List GetPackageFolder() { List resultPaceage = new List(); for (int i = 0; i < uxUpdateListPanel.Children.Count; i++) { CheckBox chkBtn = uxUpdateListPanel.Children[i] as CheckBox; if (chkBtn != null) { if (chkBtn.IsChecked == true) { if (chkBtn.Tag.ToString() != "") resultPaceage.Add(chkBtn.Tag.ToString()); } } } return resultPaceage; } private List GetUpdatePackageFilePath() { List resultPackage = new List(); // 整理要打包的東西 : 所有要打包的檔案的相對根目錄底下路徑 for (int i = 0; i < wholeDataFolder.Count; i++) { switch (wholeDataFolder[i]) { case "Basic": PackageBasicSetting(resultPackage); break; case "FrontPage": PackageFrontPageSetting(resultPackage); break; case "History": PackageHistoryWall(resultPackage, Setting.PageType.History); break; case "Product": PackageHistoryWall(resultPackage, Setting.PageType.Product); break; case "CSR": PackageHistoryWall(resultPackage, Setting.PageType.CSR); break; } } return resultPackage; } private void PackageBasicSetting(List resultPackage) { resultPackage.Add(Setting.settingJsonDataPath); } private void PackageFrontPageSetting(List resultPackage) { string[] files = Directory.GetFiles(Setting.frontPageDirectory); resultPackage.AddRange(files); } private void PackageHistoryWall(List resultPackage, Setting.PageType type) { string folderPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "Data", Setting.PageFolderName[type]); string DataConfigFileName = System.IO.Path.Combine(folderPath, @"Setting\ConfigSetting.ini"); string historyFileName = System.IO.Path.Combine(folderPath, @"HistoryWallData\HistoryFile.ini"); string DataThumFileFolder = System.IO.Path.Combine(folderPath, @"Thumb\"); string DataJsonImgFileFolder = System.IO.Path.Combine(folderPath, @"HistoryImage\"); // History : 1. Setting\ConfigSetting.ini + 2. HistoryWallData\HistoryFile.ini + 3. Thumb\bgMap file and logoMap file + 4. HistoryImage 整包 if (File.Exists(DataConfigFileName)) { resultPackage.Add(DataConfigFileName); } if (File.Exists(historyFileName)) { resultPackage.Add(historyFileName); } string[] files = Directory.GetFiles(DataThumFileFolder); foreach (string file in files) { // 只接受 bgMap 及 logoMap 兩個檔名檔案 if (GlobalFunction.CheckFileIsImage(file) && (file.IndexOf("bgMap") != -1 || file.IndexOf("logoMap") != -1)) { resultPackage.Add(file); } } if (Directory.Exists(DataJsonImgFileFolder)) { List dirs = new List(Directory.EnumerateDirectories(DataJsonImgFileFolder)); foreach (var dir in dirs) { string[] inforfiles = Directory.GetFiles(dir); foreach (string file in inforfiles) { // 接受圖片或影片 if (GlobalFunction.CheckFileIsImage(file) || GlobalFunction.CheckFileIsVideo(file)) { resultPackage.Add(file); } } } } } #endregion #region 打包 private void zipWorker_DoWork(object sender, DoWorkEventArgs e) { List packagePathList = e.Argument as List; FileStream FS = File.Create(defaultZipFileName); ZipOutputStream s = new ZipOutputStream(FS); string currentPath = Directory.GetCurrentDirectory(); s.SetLevel(9); //0 ~ 9 double stepValue = 100 / packagePathList.Count; for (int i = 0; i < packagePathList.Count; i++) { FileStream fs = File.OpenRead(packagePathList[i]); byte[] buffer = new byte[fs.Length]; fs.Read(buffer, 0, buffer.Length); string zipPath = packagePathList[i]; if (packagePathList[i].Contains(currentPath)) zipPath = zipPath.Remove(0, currentPath.Length + 1); ZipEntry entry = new ZipEntry(zipPath); entry.DateTime = DateTime.Now; entry.Size = fs.Length; fs.Close(); s.PutNextEntry(entry); s.Write(buffer, 0, buffer.Length); ChangeUpdateProgrssBarValue(stepValue); } s.Dispose(); s.Close(); } private void zipWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { StatusChange("打包完成.."); uxUpdateProgressBar.Value = 100; //切割打包資料夾 WpfDelayDoWork.DoBackgroundWork(DivisionZipFile, 1000); } #endregion #region 切割 private void divisionWorker_DoWork(object sender, DoWorkEventArgs e) { string zipFilePath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), defaultZipFileName); if (File.Exists(zipFilePath)) { try { int segmentSize = 4194304; //4MB long fileSize = new FileInfo(zipFilePath).Length; int datas = 0; int segmentCount = (int)(fileSize / (long)segmentSize); int lastSegmentSize = (int)(fileSize % (long)segmentSize); segmentCount = lastSegmentSize > 0 ? segmentCount + 1 : segmentCount; double stepValue = 100 / segmentCount; FileStream fs = File.OpenRead(zipFilePath); cutFilePaths = new List(); for (int i = 0; i < segmentCount; i++) { byte[] binaryData; string segmentFileName = zipFilePath.Replace(".zip", String.Empty) + "[" + (i + 1) + "].cut"; FileStream fOut = File.OpenWrite(segmentFileName); if (lastSegmentSize > 0 && i == segmentCount - 1) { binaryData = new byte[lastSegmentSize]; datas = fs.Read(binaryData, 0, lastSegmentSize); fOut.Write(binaryData, 0, lastSegmentSize); } else { binaryData = new byte[segmentSize]; datas = fs.Read(binaryData, 0, segmentSize); fOut.Write(binaryData, 0, segmentSize); } fOut.Flush(); fOut.Close(); ChangeUpdateProgrssBarValue(stepValue); cutFilePaths.Add(segmentFileName); } fs.Close(); File.Delete(zipFilePath); canTransFileTag = true; } catch { canTransFileTag = false; } } else { canTransFileTag = false; } } private void divisionWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { StatusChange("分割完成.."); uxUpdateProgressBar.Value = 100; if (canTransFileTag) { //傳輸更新檔案開始 WpfDelayDoWork.DoBackgroundWork(TransCutFile, 1000); return; } StatusChange("分割失敗.."); // 所有狀態回復 WpfDelayDoWork.DoBackgroundWork(InitialUpdateStatus, 3000); } #endregion #region 傳送 bool isSendComplete = false; private void updateWorker_DoWork(object sender, DoWorkEventArgs e) { if (cutFilePaths.Count > 0) { try { isSendComplete = false; // 通知分割檔案總數 KioskSocketClient.NotifyFileTransferStart(cutFilePaths.Count); // 掛上前台更新狀況事件 KioskSocketClient.SendSegmentFileFinish += KioskSocketClient_SendSegmentFileFinish; KioskSocketClient.FailSendFile += KioskSocketClient_FailSendFile; KioskSocketClient.ReceiveServerNotify += KioskSocketClient_ReceiveServerNotify; //傳送 Cut File for (int i = 0; i < cutFilePaths.Count; i++) { while (isSendComplete) { Thread.Sleep(1000); } KioskSocketClient.SendFile(cutFilePaths[i], "0"); isSendComplete = true; } } catch { KioskSocketClient = null; canTransFileTag = false; } } } private void updateWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (canTransFileTag) { //傳輸更新檔案開始 StatusChange("傳送完成.."); uxUpdateProgressBar.Value = 100; return; } StatusChange("傳送失敗.."); // 所有狀態回復 WpfDelayDoWork.DoBackgroundWork(InitialUpdateStatus, 3000); } private void KioskSocketClient_SendSegmentFileFinish(string serverIP, string index) { double stepValue = 100 / cutFilePaths.Count; ChangeUpdateProgrssBarValue(stepValue); isSendComplete = false; } #endregion #region 前台更新狀況回報 private void KioskSocketClient_FailSendFile(object sender, EventArgs e) { StatusChange("傳輸異常..."); // 所有狀態回復 WpfDelayDoWork.DoBackgroundWorkAsync(Dispatcher.CurrentDispatcher, InitialUpdateStatus, 3000); } private void KioskSocketClient_ReceiveServerNotify(string serverIP, string message) { if (message.Equals("Success")) { StatusChange("前台更新完成重新啟動中..."); // 所有狀態回復 WpfDelayDoWork.DoBackgroundWorkAsync(Dispatcher.CurrentDispatcher, InitialUpdateStatus, 3000); } else if (message.Equals("Fail")) { StatusChange("傳輸異常..."); // 所有狀態回復 WpfDelayDoWork.DoBackgroundWorkAsync(Dispatcher.CurrentDispatcher, InitialUpdateStatus, 3000); } } #endregion } public class WpfDelayDoWork { public static void DoWork(Action action, int millisecond = 300) { new Action(DoWorkAsync).BeginInvoke(Dispatcher.CurrentDispatcher, action, millisecond, null, null); } public static void DoBackgroundWork(Action action, int millisecond = 300) { new Action(DoBackgroundWorkAsync).BeginInvoke(Dispatcher.CurrentDispatcher, action, millisecond, null, null); } public static void DoWorkAsync(Dispatcher dispatcher, Action action, int millisecond) { System.Threading.Thread.Sleep(millisecond); dispatcher.BeginInvoke(action); } public static void DoBackgroundWorkAsync(Dispatcher dispatcher, Action action, int millisecond) { System.Threading.Thread.Sleep(millisecond); dispatcher.Invoke(action, DispatcherPriority.Background); } } }