2025-04-07 16:54:10 +08:00
|
|
|
using System.Net;
|
|
|
|
using System.Net.Sockets;
|
|
|
|
using System.Text;
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
using System.IO; // 為 Path 和 File 提供支持
|
|
|
|
using DBObj;
|
|
|
|
using OverlayFormObj;
|
2025-05-14 16:25:24 +08:00
|
|
|
using HeartbeatSender;
|
2025-04-07 16:54:10 +08:00
|
|
|
namespace DualScreenDemo
|
|
|
|
{
|
|
|
|
public class TCPServer
|
|
|
|
{
|
|
|
|
private TcpListener listener;
|
|
|
|
private const int Port = 1000;
|
|
|
|
private readonly string hostNameSuffix;
|
|
|
|
//private bool isProcessingCommand = false;
|
2025-07-04 18:19:41 +08:00
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
|
|
|
public TCPServer()
|
|
|
|
{
|
|
|
|
listener = new TcpListener(IPAddress.Any, Port);
|
|
|
|
hostNameSuffix = GetHostNameSuffix();
|
2025-07-04 18:19:41 +08:00
|
|
|
|
|
|
|
_ = Task.Run(() => Start());
|
2025-04-07 16:54:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private bool IsFormReady(Form form)
|
|
|
|
{
|
|
|
|
if (form == null) return false;
|
|
|
|
bool isReady = false;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (form.IsHandleCreated && !form.IsDisposed)
|
|
|
|
{
|
|
|
|
if (form.InvokeRequired)
|
|
|
|
{
|
|
|
|
form.Invoke(new Action(() => isReady = true));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
isReady = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
isReady = false;
|
|
|
|
}
|
|
|
|
return isReady;
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task SafeInvoke(Form form, Action action, int maxRetries = 10)
|
|
|
|
{
|
|
|
|
if (form == null) return;
|
|
|
|
|
|
|
|
for (int i = 0; i < maxRetries; i++)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (IsFormReady(form))
|
|
|
|
{
|
|
|
|
if (form.InvokeRequired)
|
|
|
|
{
|
|
|
|
form.Invoke(action);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
action();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
Console.WriteLine($"Invoke attempt {i + 1} failed: {ex.Message}");
|
|
|
|
}
|
|
|
|
|
|
|
|
await Task.Delay(500); // 等待500毫秒后重试
|
|
|
|
}
|
|
|
|
Console.WriteLine("Failed to invoke action after maximum retries");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
{
|
|
|
|
listener.Start();
|
|
|
|
Console.WriteLine("Server started on port " + Port + ".");
|
2025-05-14 16:25:24 +08:00
|
|
|
// 心跳封包
|
|
|
|
//heartbeatSender sender = new heartbeatSender("127.0.0.1", 8888);
|
|
|
|
//sender.Start();
|
|
|
|
// Console.WriteLine($"heart beat for server{sender.RemoteEndPoint.Address}:{sender.RemoteEndPoint.Port}");
|
2025-06-18 10:43:11 +08:00
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
try {
|
2025-07-03 18:15:21 +08:00
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
Console.WriteLine("Waiting for connections...");
|
|
|
|
using (TcpClient client = listener.AcceptTcpClient())
|
|
|
|
{
|
|
|
|
Console.WriteLine("Connected!");
|
|
|
|
NetworkStream stream = client.GetStream();
|
|
|
|
|
|
|
|
while (client.Connected)
|
|
|
|
{
|
|
|
|
byte[] buffer = new byte[1024];
|
|
|
|
int bytesRead = stream.Read(buffer, 0, buffer.Length);
|
|
|
|
if (bytesRead == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
string request = Encoding.UTF8.GetString(buffer, 0, bytesRead);
|
|
|
|
Console.WriteLine("Received: " + request.Trim());
|
|
|
|
|
|
|
|
if (request.Length < 5)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
string requestHostSuffix = request.Substring(0, 3);
|
|
|
|
string command = request.Substring(4);
|
|
|
|
|
|
|
|
if (requestHostSuffix.Equals(hostNameSuffix, StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
2025-05-08 15:20:54 +08:00
|
|
|
// 新增監聽指令
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
if (command.Trim().Equals("X", StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
2025-05-19 13:23:09 +08:00
|
|
|
PrimaryForm.Instance.pictureBoxQRCode.Visible = false;
|
|
|
|
PrimaryForm.Instance.closeQRCodeButton.Visible = false;
|
2025-04-07 16:54:10 +08:00
|
|
|
_ = SafeInvoke(VideoPlayerForm.Instance, async () =>
|
|
|
|
{
|
|
|
|
if (IsFormReady(PrimaryForm.Instance))
|
|
|
|
{
|
2025-05-19 13:23:09 +08:00
|
|
|
await SafeInvoke(PrimaryForm.Instance, async () =>
|
2025-04-07 16:54:10 +08:00
|
|
|
{
|
|
|
|
PrimaryForm.Instance.ShowSendOffScreen();
|
|
|
|
Console.WriteLine("開始設置新的播放列表");
|
2025-06-26 14:28:23 +08:00
|
|
|
|
2025-06-18 10:43:11 +08:00
|
|
|
|
2025-06-17 09:27:24 +08:00
|
|
|
string closePath = @"D:\video\CLOSE.MPG";
|
2025-04-07 16:54:10 +08:00
|
|
|
if (File.Exists(closePath))
|
|
|
|
{
|
2025-07-08 13:37:12 +08:00
|
|
|
SongData closeSong = new SongData("0", "結束播放",closePath, 1,true);
|
2025-04-09 11:12:59 +08:00
|
|
|
VideoPlayerForm.publicPlaylist = new List<SongData>();
|
2025-04-07 16:54:10 +08:00
|
|
|
VideoPlayerForm.playingSongList = new List<SongData>();
|
2025-05-05 11:02:56 +08:00
|
|
|
PrimaryForm.playedSongsHistory = new List<SongData>();
|
2025-04-07 16:54:10 +08:00
|
|
|
if (VideoPlayerForm.Instance.currentPlayingSong != null)
|
|
|
|
{
|
|
|
|
VideoPlayerForm.playingSongList.Add(VideoPlayerForm.Instance.currentPlayingSong);
|
|
|
|
}
|
2025-06-18 10:43:11 +08:00
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
VideoPlayerForm.playingSongList.Add(closeSong);
|
2025-04-09 11:12:59 +08:00
|
|
|
VideoPlayerForm.publicPlaylist.Add(closeSong);
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
PrimaryForm.userRequestedSongs = new List<SongData>();
|
|
|
|
|
|
|
|
if (IsFormReady(OverlayForm.MainForm))
|
|
|
|
{
|
|
|
|
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
|
|
|
}
|
2025-04-09 11:12:59 +08:00
|
|
|
|
|
|
|
VideoPlayerForm.Instance.PlayNextSong();
|
2025-05-08 10:09:14 +08:00
|
|
|
PrimaryForm.Instance.logout();
|
2025-04-07 16:54:10 +08:00
|
|
|
Console.WriteLine("已設置新的播放列表,包含當前歌曲和 CLOSE.MPG");
|
2025-05-19 13:23:09 +08:00
|
|
|
try
|
|
|
|
{
|
|
|
|
await HttpServer.RestartServer();
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
Console.WriteLine("RestartServer 發生錯誤: " + ex.Message);
|
|
|
|
}
|
2025-04-07 16:54:10 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Console.WriteLine($"錯誤: 找不到檔案 {closePath}");
|
|
|
|
}
|
2025-06-26 14:28:23 +08:00
|
|
|
OverlayForm.Instance.ResetMarqueeTextToWelcomeMessage();
|
2025-04-07 16:54:10 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-07-03 18:15:21 +08:00
|
|
|
Program.RoomState = "CLOSE";
|
2025-05-19 14:27:48 +08:00
|
|
|
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
|
|
|
|
stream.Write(okResponse, 0, okResponse.Length);
|
2025-05-08 10:09:14 +08:00
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (command.Trim().Equals("O", StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
2025-05-08 10:09:14 +08:00
|
|
|
// 開台時跳至首頁
|
2025-06-18 10:43:11 +08:00
|
|
|
|
2025-04-09 11:12:59 +08:00
|
|
|
VideoPlayerForm.publicPlaylist = new List<SongData>();
|
|
|
|
VideoPlayerForm.playingSongList = new List<SongData>();
|
|
|
|
VideoPlayerForm.Instance.PlayPublicPlaylist();
|
2025-06-02 09:41:07 +08:00
|
|
|
PrimaryForm.currentSongIndexInHistory = -1;
|
|
|
|
PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty);
|
2025-07-03 18:15:21 +08:00
|
|
|
Program.RoomState = "OPEN";
|
2025-06-18 10:43:11 +08:00
|
|
|
PrimaryForm.Instance.HideSendOffScreen();
|
2025-06-26 14:28:23 +08:00
|
|
|
|
|
|
|
OverlayForm.Instance.ResetMarqueeTextToWelcomeMessage();
|
2025-06-18 10:43:11 +08:00
|
|
|
|
2025-05-19 14:27:48 +08:00
|
|
|
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
|
|
|
|
stream.Write(okResponse, 0, okResponse.Length);
|
2025-04-07 16:54:10 +08:00
|
|
|
continue;
|
|
|
|
}
|
2025-05-08 15:20:54 +08:00
|
|
|
if (command.Trim().Equals("F", StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
|
|
|
_ = SafeInvoke(PrimaryForm.Instance, () =>
|
|
|
|
{
|
|
|
|
PrimaryForm.Instance.ShowSendOffScreen();
|
|
|
|
VideoPlayerForm.Instance.Pause();
|
|
|
|
string marqueeMessage = "發生火災,請跟隨引導至逃生出口!!!";
|
|
|
|
OverlayForm.MainForm.UpdateMarqueeText(marqueeMessage, OverlayForm.MarqueeStartPosition.Middle, Color.Red);
|
|
|
|
});
|
|
|
|
|
|
|
|
// 更新狀態檔案(可選,若你要記錄狀態)
|
2025-07-03 18:15:21 +08:00
|
|
|
Program.RoomState = "PAUSE";
|
2025-05-19 14:27:48 +08:00
|
|
|
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
|
|
|
|
stream.Write(okResponse, 0, okResponse.Length);
|
2025-05-08 15:20:54 +08:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2025-06-18 10:43:11 +08:00
|
|
|
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (IsFormReady(OverlayForm.MainForm))
|
|
|
|
{
|
|
|
|
string message = request.Trim();
|
|
|
|
string pattern = @"^(全部|\d{4})\((白色|紅色|綠色|黑色|藍色)\)-";
|
|
|
|
Match match = Regex.Match(message, pattern);
|
|
|
|
_ = SafeInvoke(OverlayForm.MainForm, () =>
|
|
|
|
{
|
|
|
|
if (match.Success)
|
|
|
|
{
|
|
|
|
string marqueeMessage = message.Substring(match.Value.Length).Trim();
|
|
|
|
Color textColor = GetColorFromString(match.Groups[2].Value);
|
2025-06-26 14:28:23 +08:00
|
|
|
OverlayForm.MainForm.UpdateMarqueeText(marqueeMessage, OverlayForm.MarqueeStartPosition.Right, textColor);
|
2025-04-07 16:54:10 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
string marqueeMessage = "系統公告: " + message;
|
|
|
|
OverlayForm.MainForm.UpdateMarqueeTextSecondLine(marqueeMessage);
|
|
|
|
}
|
|
|
|
});
|
2025-06-26 14:28:23 +08:00
|
|
|
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
|
|
|
|
stream.Write(okResponse, 0, okResponse.Length);
|
2025-04-07 16:54:10 +08:00
|
|
|
}
|
|
|
|
|
2025-07-03 18:15:21 +08:00
|
|
|
/*
|
2025-04-07 16:54:10 +08:00
|
|
|
if (request.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2025-07-03 18:15:21 +08:00
|
|
|
*/
|
2025-04-07 16:54:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Console.WriteLine("Connection closed.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Console.WriteLine($"Error: {e.Message}");
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
listener.Stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private Color GetColorFromString(string colorName)
|
|
|
|
{
|
|
|
|
switch (colorName)
|
|
|
|
{
|
|
|
|
case "白色":
|
|
|
|
return Color.White;
|
|
|
|
case "紅色":
|
|
|
|
return Color.Red;
|
|
|
|
case "綠色":
|
|
|
|
return Color.LightGreen;
|
|
|
|
case "黑色":
|
|
|
|
return Color.Black;
|
|
|
|
case "藍色":
|
|
|
|
return Color.LightBlue;
|
|
|
|
default:
|
|
|
|
return Color.Black;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private string GetHostNameSuffix()
|
|
|
|
{
|
|
|
|
string hostName = Dns.GetHostName();
|
|
|
|
return hostName.Length > 3 ? hostName.Substring(hostName.Length - 3) : hostName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|