From c623bbe26da172c58a9c67161c28ab630765978e Mon Sep 17 00:00:00 2001 From: jasonchenwork Date: Tue, 3 Jun 2025 16:52:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BF=83=E8=B7=B3=E5=B0=81=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HeartbeatSender.cs | 141 +++++++++++++++---- PrimaryFormParts/PrimaryForm.SQLSearch.cs | 59 ++++++-- PrimaryFormParts/PrimaryForm.cs | 25 ++-- Program.cs | 156 +++++++++++++--------- superstar_1.0.0.csproj.user | 3 - 5 files changed, 269 insertions(+), 115 deletions(-) diff --git a/HeartbeatSender.cs b/HeartbeatSender.cs index eafde62..2317fc2 100644 --- a/HeartbeatSender.cs +++ b/HeartbeatSender.cs @@ -1,39 +1,132 @@ +using System.Net.Http; +using System.Text; +using System.Text.Json; // 適用於 .NET Core 3.0+ / .NET 5/6/7/8 using System.Net; using System.Net.Sockets; -using System.Text; -using System.Timers; -namespace HeartbeatSender{ + +namespace HeartbeatSender +{ + + public class HeartbeatData + { + public string branch_name { get; set; } + public string room_name { get; set; } + public string room_ip { get; set; } + public string email { get; set; } + public string password { get; set; } + } public class heartbeatSender { - private readonly UdpClient udpClient; - private readonly IPEndPoint remoteEndPoint; - private readonly System.Timers.Timer heartbeatTimer; - public IPEndPoint RemoteEndPoint => remoteEndPoint; - public heartbeatSender(string targetIp, int targetPort, int intervalMilliseconds = 3000) + private readonly HttpClient httpClient = new HttpClient(); + private string token; + private string heartbeatUrl; + + public heartbeatSender(string heartbeatUrl) { - udpClient = new UdpClient(); - // 設置 IP 和 PORT - remoteEndPoint = new IPEndPoint(IPAddress.Parse(targetIp), targetPort); - // 每3秒發送一次 - heartbeatTimer = new System.Timers.Timer(intervalMilliseconds); - heartbeatTimer.Elapsed += SendHeartbeat; - heartbeatTimer.AutoReset = true; + this.heartbeatUrl = heartbeatUrl; } - private void SendHeartbeat(object sender, ElapsedEventArgs e) + public static string GetLocalIPv4() { + foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList) + { + if (ip.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(ip)) + { + return ip.ToString(); + } + } + return "127.0.0.1"; // fallback + } + public async Task LoginAndGetTokenAsync() + { + var loginUrl = "http://zqd.superstar.dnsnet.cc/api/room/receiveRegister"; + string hostName = System.Net.Dns.GetHostName(); + + var loginPayload = new + { + branch_name = "測試", + room_name = "PC" + hostName.Substring(Math.Max(0, hostName.Length - 3)), + room_ip = GetLocalIPv4(), + email = "MachineKTV@gmail.com", + password = "aa147258-" + }; + + var json = JsonSerializer.Serialize(loginPayload); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + try { - string heartbeatMessage = "HEARTBEAT"; - byte[] data = Encoding.UTF8.GetBytes(heartbeatMessage); - udpClient.Send(data, data.Length, remoteEndPoint); - Console.WriteLine($"Heartbeat sent to {remoteEndPoint.Address}:{remoteEndPoint.Port}"); + var response = await httpClient.PostAsync(loginUrl, content); + response.EnsureSuccessStatusCode(); + + var responseJson = await response.Content.ReadAsStringAsync(); + // Console.WriteLine("API 回傳內容:" + responseJson); + + using var doc = JsonDocument.Parse(responseJson); + if (doc.RootElement.TryGetProperty("data", out JsonElement dataElement) && + dataElement.ValueKind == JsonValueKind.Object) + { + if (dataElement.TryGetProperty("token", out JsonElement tokenElement)) + { + token = tokenElement.GetString(); + } + } + // Console.WriteLine("登入成功,取得 token:" + token); + return true; } catch (Exception ex) { - Console.WriteLine($"Error sending heartbeat: {ex.Message}"); + Console.WriteLine($"登入失敗:{ex.Message}"); + return false; } } - public void Start() => heartbeatTimer.Start(); - public void Stop() => heartbeatTimer.Stop(); + + public async Task SendHeartbeatAsync() + { + if (string.IsNullOrEmpty(token)) + { + Console.WriteLine("請先登入取得 token"); + return; + } + //Console.WriteLine(GetLocalIPv4()); + string hostName = System.Net.Dns.GetHostName(); + + var heartbeatData = new + { + branch_name = "測試", + hostname = "PC" + hostName.Substring(Math.Max(0, hostName.Length - 3)), + ip = GetLocalIPv4(), + }; + + var json = JsonSerializer.Serialize(heartbeatData); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + heartbeatUrl = "http://zqd.superstar.dnsnet.cc/api/room/heartbeat"; + var request = new HttpRequestMessage(HttpMethod.Post, heartbeatUrl); + request.Content = content; + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); + // Console.WriteLine("送出的 JSON:"); + // Console.WriteLine(json); + var response = await httpClient.SendAsync(request); + try + { + + // Console.WriteLine($"心跳送出狀態:{response.StatusCode}"); + + response.EnsureSuccessStatusCode(); + + var responseJson = await response.Content.ReadAsStringAsync(); + + using var doc = JsonDocument.Parse(responseJson); + // Console.WriteLine("API 回傳內容:" + responseJson); + + } + catch (Exception ex) + { + var errorContent = await response.Content.ReadAsStringAsync(); + Console.WriteLine($"送出心跳錯誤:{ex.Message}"); + Console.WriteLine($"後端回應內容:{errorContent}"); + } + } + } -} \ No newline at end of file + +} diff --git a/PrimaryFormParts/PrimaryForm.SQLSearch.cs b/PrimaryFormParts/PrimaryForm.SQLSearch.cs index e2ec3d3..f294d30 100644 --- a/PrimaryFormParts/PrimaryForm.SQLSearch.cs +++ b/PrimaryFormParts/PrimaryForm.SQLSearch.cs @@ -6,14 +6,50 @@ using System.Diagnostics; namespace DualScreenDemo{ public partial class PrimaryForm { + private static string connectionStringfortest = ""; + public static void LoadConnectionStringFromFile(string filePath) + { + filePath = Path.Combine(Application.StartupPath, "txt", filePath); + try + { + if (File.Exists(filePath)) + { + string fileContent = File.ReadAllText(filePath).Trim(); + + if (!string.IsNullOrWhiteSpace(fileContent)) + { + connectionStringfortest = fileContent; + Console.WriteLine("連線字串載入成功。"); + } + else + { + Console.WriteLine("檔案內容為空。"); + } + } + else + { + Console.WriteLine("找不到指定的檔案:" + filePath); + } + } + catch (Exception ex) + { + Console.WriteLine("讀取檔案時發生錯誤:" + ex.Message); + } + } + + private static string GetConnectionString() + { + return connectionStringfortest; + } + public bool isLoggedIn = false; public string userPhone = string.Empty; public List SearchSongs_Mysql(string query) { List searchResults = new List(); Console.WriteLine(query); - string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; - + //string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; + string connectionString = GetConnectionString(); using (var connection = new MySqlConnection(connectionString)) { Stopwatch stopwatch = new Stopwatch(); @@ -65,8 +101,8 @@ namespace DualScreenDemo{ public static List SearchSingers_Mysql(string query){ List searchResults = new List(); Console.WriteLine(query); - string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; - + //string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; + string connectionString = GetConnectionString(); using (var connection = new MySqlConnection(connectionString)) { Stopwatch stopwatch = new Stopwatch(); @@ -100,8 +136,8 @@ namespace DualScreenDemo{ string query = $"INSERT INTO FavoriteSongs (userPhone,songNumber) VALUES ('{userPhone}','{songNumber}');"; Console.WriteLine(query); - string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; - + //string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; + string connectionString = GetConnectionString(); using (var connection = new MySqlConnection(connectionString)) { @@ -128,8 +164,8 @@ namespace DualScreenDemo{ string songlist = phonenumber + "的歌單"; string query = $"INSERT INTO FavoriteSongs (userPhone,songNumber) VALUES ('{phonenumber}','{songlist}');"; Console.WriteLine(query); - string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; - + //string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; + string connectionString = GetConnectionString(); using (var connection = new MySqlConnection(connectionString)) { @@ -163,7 +199,8 @@ namespace DualScreenDemo{ } public bool checkPhoneNumberExist(string phonenumber){ - string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; + //string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; + string connectionString = GetConnectionString(); bool exists = false; using (var connection = new MySqlConnection(connectionString)) { @@ -212,8 +249,8 @@ namespace DualScreenDemo{ public void AddSongCount(string id){ string query = $"UPDATE songs SET song_counts = song_counts+1 WHERE id = '{id}';"; Console.WriteLine(query); - string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; - + //string connectionString = "Server=192.168.11.4;Port=3306;Database=Karaoke-Kingpin;User=Karaoke-Kingpin;Password=ESM7yTPMnavFmbBH;"; + string connectionString = GetConnectionString(); using (var connection = new MySqlConnection(connectionString)) { diff --git a/PrimaryFormParts/PrimaryForm.cs b/PrimaryFormParts/PrimaryForm.cs index ec9833b..3446797 100644 --- a/PrimaryFormParts/PrimaryForm.cs +++ b/PrimaryFormParts/PrimaryForm.cs @@ -184,31 +184,31 @@ namespace DualScreenDemo public PrimaryForm() { Instance = this; - + // 添加 DPI 感知支持 if (Environment.OSVersion.Version.Major >= 6) { SetProcessDPIAware(); } - + this.DoubleBuffered = true; - + InitializeProgressBar(); // 初始化自动刷新Timer autoRefreshTimer = new Timer(); autoRefreshTimer.Interval = 1000; // 1秒 autoRefreshTimer.Tick += AutoRefreshTimer_Tick; - + // 設置窗體屬性 this.WindowState = FormWindowState.Maximized; this.FormBorderStyle = FormBorderStyle.None; - + lightControlTimer = new Timer(); - lightControlTimer.Interval = 5; + lightControlTimer.Interval = 5; lightControlTimer.Tick += LightControlTimer_Tick; volumeUpTimer = new Timer(); - volumeUpTimer.Interval = 100; + volumeUpTimer.Interval = 100; volumeUpTimer.Tick += VolumeUpTimer_Tick; volumeDownTimer = new Timer(); volumeDownTimer.Interval = 100; @@ -216,12 +216,12 @@ namespace DualScreenDemo micControlTimer = new Timer(); micControlTimer.Interval = 100; micControlTimer.Tick += MicControlTimer_Tick; - + InitializeRecording(); InitializeMediaPlayer(); LoadSongData(); - LoadImages(); - InitializeFormAndControls(); + LoadImages(); + InitializeFormAndControls(); InitializeMultiPagePanel(); OverlayQRCodeOnImage(HttpServer.randomFolderPath); @@ -229,16 +229,17 @@ namespace DualScreenDemo InitializeHandWritingForSongs(); InitializeSendOffPanel(); - + InitializePromotionsAndMenuPanel(); SaveInitialControlStates(this); // 包廂 + port 名稱 this.Paint += PrimaryForm_Paint; this.Paint += User_Paint; // 註冊多頁面面板的頁碼改變事件處理 - multiPagePanel.PageIndexChanged += HandlePageChanged; + multiPagePanel.PageIndexChanged += HandlePageChanged; // 添加 Load 事件處理 this.Load += PrimaryForm_Load; + LoadConnectionStringFromFile("test.env"); } diff --git a/Program.cs b/Program.cs index c4712d3..45a6305 100644 --- a/Program.cs +++ b/Program.cs @@ -2,6 +2,7 @@ using System.IO; using Microsoft.Win32; using System.Diagnostics; using DBObj; +using HeartbeatSender; namespace DualScreenDemo { @@ -14,76 +15,101 @@ namespace DualScreenDemo private static PrimaryForm primaryForm; // 儲存實例的參考 [STAThread] -static void Main() -{ - try - { - // COM 初始化 - int hr = ComInterop.CoInitializeEx(IntPtr.Zero, ComInterop.COINIT_APARTMENTTHREADED); - if (hr < 0) + static void Main() { - Console.WriteLine("Failed to initialize COM library."); - return; - } - // 初始化管理器 + var sender = new HeartbeatSender.heartbeatSender("http://zqd.superstar.dnsnet.cc/api/room/receiveRegister"); - songListManager = new SongListManager(); // 使用单例 - //artistManager = new ArtistManager(); + // 同步呼叫非同步登入取得 token + bool loginSuccess = sender.LoginAndGetTokenAsync().GetAwaiter().GetResult(); - var commandHandler = new CommandHandler(songListManager); - serialPortManager = new SerialPortManager(commandHandler); - serialPortManager.InitializeSerialPort(); - - // 輸出屏幕信息 - Console.WriteLine($"Virtual Screen: {SystemInformation.VirtualScreen}"); - foreach (var screen in Screen.AllScreens) - { - Console.WriteLine($"Screen: {screen.DeviceName} Resolution: {screen.Bounds.Width}x{screen.Bounds.Height}"); - } - - // 啟動服務器 - Task.Run(() => HttpServerManager.StartServer()); - Task.Run(() => TCPServerManager.StartServer()); - - // 註冊事件 - Application.ApplicationExit += (sender, e) => SerialPortManager.CloseSerialPortSafely(); - SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged; - - // 創建主窗體 - primaryForm = new PrimaryForm(); - //primaryForm.allSongs = songListManager.AllSongs; - //primaryForm.allArtists = artistManager.AllArtists; - primaryForm.StartPosition = FormStartPosition.Manual; - primaryForm.Location = new Point(0, 0); - primaryForm.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); - - // 在完整初始化後檢查狀態 - string stateFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"txt","states.txt"); - bool isClosedState = File.Exists(stateFilePath) && - File.ReadAllText(stateFilePath).Trim().Equals("CLOSE", StringComparison.OrdinalIgnoreCase); - - InitializeSecondaryScreen(); - - // 使用 Shown 事件來確保窗體完全加載後再顯示送客畫面 - if (isClosedState) - { - primaryForm.Shown += (s, e) => + if (loginSuccess) { - primaryForm.ShowSendOffScreen(); - }; - } + // 先送一次心跳 (同步呼叫) + sender.SendHeartbeatAsync().GetAwaiter().GetResult(); - primaryForm.Show(); - Application.Run(primaryForm); - } - catch (Exception ex) - { - WriteLog(ex.ToString()); - } - finally - { - SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged; - } + // 背景持續每3秒送心跳 + _ = Task.Run(async () => + { + while (true) + { + await sender.SendHeartbeatAsync(); + await Task.Delay(3000); // 每3秒送一次 + } + }); + } + else + { + Console.WriteLine("登入失敗,無法送出心跳"); + } + + try + { + // COM 初始化 + int hr = ComInterop.CoInitializeEx(IntPtr.Zero, ComInterop.COINIT_APARTMENTTHREADED); + if (hr < 0) + { + Console.WriteLine("Failed to initialize COM library."); + return; + } + // 初始化管理器 + + songListManager = new SongListManager(); // 使用单例 + //artistManager = new ArtistManager(); + + var commandHandler = new CommandHandler(songListManager); + serialPortManager = new SerialPortManager(commandHandler); + serialPortManager.InitializeSerialPort(); + + // 輸出屏幕信息 + Console.WriteLine($"Virtual Screen: {SystemInformation.VirtualScreen}"); + foreach (var screen in Screen.AllScreens) + { + Console.WriteLine($"Screen: {screen.DeviceName} Resolution: {screen.Bounds.Width}x{screen.Bounds.Height}"); + } + + // 啟動服務器 + Task.Run(() => HttpServerManager.StartServer()); + Task.Run(() => TCPServerManager.StartServer()); + + // 註冊事件 + Application.ApplicationExit += (sender, e) => SerialPortManager.CloseSerialPortSafely(); + SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged; + + // 創建主窗體 + primaryForm = new PrimaryForm(); + //primaryForm.allSongs = songListManager.AllSongs; + //primaryForm.allArtists = artistManager.AllArtists; + primaryForm.StartPosition = FormStartPosition.Manual; + primaryForm.Location = new Point(0, 0); + primaryForm.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); + + // 在完整初始化後檢查狀態 + string stateFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "txt", "states.txt"); + bool isClosedState = File.Exists(stateFilePath) && + File.ReadAllText(stateFilePath).Trim().Equals("CLOSE", StringComparison.OrdinalIgnoreCase); + + InitializeSecondaryScreen(); + + // 使用 Shown 事件來確保窗體完全加載後再顯示送客畫面 + if (isClosedState) + { + primaryForm.Shown += (s, e) => + { + primaryForm.ShowSendOffScreen(); + }; + } + + primaryForm.Show(); + Application.Run(primaryForm); + } + catch (Exception ex) + { + WriteLog(ex.ToString()); + } + finally + { + SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged; + } } diff --git a/superstar_1.0.0.csproj.user b/superstar_1.0.0.csproj.user index 640d365..01efbab 100644 --- a/superstar_1.0.0.csproj.user +++ b/superstar_1.0.0.csproj.user @@ -176,9 +176,6 @@ Form - - Form - Form