調整 DB 物件

調整 Room states 改讀DB
修正 字體統一 20
20250728
This commit is contained in:
jasonchenwork 2025-07-28 16:22:24 +08:00
parent 36ab52bac1
commit 4808f02501
10 changed files with 258 additions and 106 deletions

View File

@ -16,9 +16,9 @@ namespace DualScreenDemo
private int _wrongInputCountfor62 = 0; // 錯誤輸入計數器 private int _wrongInputCountfor62 = 0; // 錯誤輸入計數器
private int _wrongInputCountfor61 = 0; // 錯誤輸入計數器 private int _wrongInputCountfor61 = 0; // 錯誤輸入計數器
private const int MaxWrongLimit = 3; // 錯誤輸入限制次數 private const int MaxWrongLimit = 3; // 錯誤輸入限制次數
private readonly SongListManager songListManager; private readonly SQLManager songListManager;
public CommandHandler(SongListManager songListManager) public CommandHandler(SQLManager songListManager)
{ {
this.songListManager = songListManager; this.songListManager = songListManager;
} }

View File

@ -6,42 +6,166 @@ namespace DBObj
{ {
public class MyDB : IDisposable public class MyDB : IDisposable
{ {
private readonly string connectionString = Utils.Env.GetDBConnection(); private readonly string connectionString = Utils.Env.GetDBConnection();
private MySqlConnection conn; private MySqlConnection conn;
private DataTable table = new DataTable();
private int cursor = 0;
public MyDB() public MyDB()
{ {
conn = new MySqlConnection(connectionString); conn = new MySqlConnection(connectionString);
conn.Open(); conn.Open();
Console.WriteLine("MyDB 連線成功!"); //Console.WriteLine("MyDB 連線成功!");
} }
public void Dispose() public void Dispose()
{ {
if (conn != null) if (conn != null)
{ {
conn.Close(); conn.Dispose(); // 包含 Close + 資源釋放
Console.WriteLine("MyDB 連線已關閉!"); conn = null;
//Console.WriteLine("MyDB 連線已釋放!");
} }
} }
// SELECT 方法 // SELECT 方法
public DataTable Select(string query, MySqlParameter[] parameters) public bool open(string query, MySqlParameter[] parameters, bool showError = true)
{ {
using (var cmd = new MySqlCommand(query, conn)) table.Clear();
cursor = 0;
try
{ {
if (parameters != null) using (var cmd = new MySqlCommand(query, conn))
{ {
cmd.Parameters.AddRange(parameters); if (parameters != null)
} {
using (var adapter = new MySqlDataAdapter(cmd)) cmd.Parameters.AddRange(parameters);
{ }
DataTable result = new DataTable(); using (var adapter = new MySqlDataAdapter(cmd))
adapter.Fill(result); {
return result; adapter.Fill(table);
}
} }
} }
catch (Exception ex)
{
if (showError)
Console.WriteLine($"DB Error: {ex.Message}");
return false;
}
return true;
} }
public T Field<T>(string name)
{
if (cursor < 0 || cursor >= table.Rows.Count || !table.Columns.Contains(name))
return default;
object value = table.Rows[cursor][name];
if (value == DBNull.Value)
return default;
return (T)Convert.ChangeType(value, typeof(T));
}
public List<T> ToList<T>() where T : new()
{
var list = new List<T>();
foreach (DataRow row in table.Rows)
{
T obj = new T();
foreach (DataColumn col in table.Columns)
{
var prop = typeof(T).GetProperty(col.ColumnName,
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.IgnoreCase);
if (prop != null && prop.CanWrite)
{
object value = row[col];
if (value == DBNull.Value)
{
value = null;
}
try
{
if (value != null && prop.PropertyType != value.GetType())
{
value = Convert.ChangeType(value, prop.PropertyType);
}
prop.SetValue(obj, value);
}
catch
{
// 可以視情況加入錯誤處理
}
}
}
list.Add(obj);
}
return list;
}
public Dictionary<string, object> CurrentRowAsDictionary()
{
if (cursor < 0 || cursor >= table.Rows.Count)
return null;
var dict = new Dictionary<string, object>();
foreach (DataColumn col in table.Columns)
{
dict[col.ColumnName] = table.Rows[cursor][col];
}
return dict;
}
public void moveNext()
{
cursor++;
}
public void movePrevious()
{
cursor--;
}
public void move(int index)
{
cursor = index;
}
public void moveFirst()
{
cursor = 0;
}
public void moveEnd()
{
cursor = table.Rows.Count - 1;
}
public bool bof()
{
return (cursor < 0) || (table.Rows.Count == 0);
}
public bool eof()
{
return cursor > table.Rows.Count - 1;
}
public bool Read()
{
if (eof()) return false;
moveNext();
return !eof();
}
public int recordCount()
{
return table.Rows.Count;
}
public bool found()
{
return table.Rows.Count > 0;
}
public void clear()
{
table.Clear();
}
// INSERT / UPDATE / DELETE 方法 // INSERT / UPDATE / DELETE 方法
public int ExecuteNonQuery(string query, MySqlParameter[] parameters) public int ExecuteNonQuery(string query, MySqlParameter[] parameters)
@ -55,5 +179,12 @@ namespace DBObj
return cmd.ExecuteNonQuery(); return cmd.ExecuteNonQuery();
} }
} }
public long LastInsertId()
{
using (var cmd = new MySqlCommand("SELECT LAST_INSERT_ID()", conn))
{
return Convert.ToInt64(cmd.ExecuteScalar());
}
}
} }
} }

View File

@ -2,13 +2,13 @@ using System.Data;
using DualScreenDemo; using DualScreenDemo;
namespace DBObj namespace DBObj
{ {
public class SongListManager public class SQLManager
{ {
private MyDB db = new MyDB(); private MyDB db = new MyDB();
public List<SongData> FavoriteSongs { get; private set; } public List<SongData> FavoriteSongs { get; private set; }
//public bool IsUserLoggedIn { get; set; } //public bool IsUserLoggedIn { get; set; }
//public string UserPhoneNumber { get; set; } //public string UserPhoneNumber { get; set; }
public SongListManager() public SQLManager()
{ {
FavoriteSongs = new List<SongData>(); FavoriteSongs = new List<SongData>();
} }
@ -52,53 +52,55 @@ namespace DBObj
} }
} }
*/ */
public List<SongData> SearchNewSongs(){ public List<SongData> SearchNewSongs()
string query= $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};"; {
return PrimaryForm.Instance.SearchSongs_Mysql(query); string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};";
return select_Mysql(query);
} }
public List<SongData> SearchHotSongs(){ public List<SongData> SearchHotSongs(){
string query= $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY song_counts DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};"; string query= $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY song_counts DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};";
return PrimaryForm.Instance.SearchSongs_Mysql(query); return select_Mysql(query);
} }
public List<SongData> SearchSongsBySinger(string keyword) public List<SongData> SearchSongsBySinger(string keyword)
{ {
Console.WriteLine("keyword : " + keyword); Console.WriteLine("keyword : " + keyword);
var keywordLower = keyword.ToLower(); var keywordLower = keyword.ToLower();
string query = $"SELECT * FROM song_library_cache WHERE artistA LIKE '%{keywordLower}%' OR artistB LIKE'%{keywordLower}%'ORDER BY song_counts DESC;"; string query = $"SELECT * FROM song_library_cache WHERE artistA LIKE '%{keywordLower}%' OR artistB LIKE'%{keywordLower}%'ORDER BY song_counts DESC;";
return PrimaryForm.Instance.SearchSongs_Mysql(query); return select_Mysql(query);
} }
public List<SongData> SearchSongsByName(string keyword) public List<SongData> SearchSongsByName(string keyword)
{ {
string query = $"SELECT * FROM song_library_cache WHERE LOWER(song_name) LIKE CONCAT('%', LOWER('{keyword}'), '%');"; string query = $"SELECT * FROM song_library_cache WHERE LOWER(song_name) LIKE CONCAT('%', LOWER('{keyword}'), '%');";
return PrimaryForm.Instance.SearchSongs_Mysql(query); return select_Mysql(query);
} }
public SongData SearchSongByNumber(string songNumber) public SongData SearchSongByNumber(string songNumber)
{ {
string query = $"SELECT * FROM song_library_cache WHERE song_id = '{songNumber}';"; string query = $"SELECT * FROM song_library_cache WHERE song_id = '{songNumber}';";
var searchResults =PrimaryForm.Instance.SearchSongs_Mysql(query); var searchResults =select_Mysql(query);
return searchResults.FirstOrDefault(); return searchResults.FirstOrDefault();
} }
private List<SongData> select_Mysql(string query) private List<SongData> select_Mysql(string query)
{ {
List<SongData> searchResults = new List<SongData>(); List<SongData> searchResults = new List<SongData>();
Console.WriteLine(query); Console.WriteLine(query);
using (db) using (var db = new MyDB())
{ {
DataTable result = db.Select(query, null); if (db.open(query, null)) {
foreach (DataRow row in result.Rows) while (db.Read()){
{ searchResults.Add(new SongData(
searchResults.Add(new SongData( db.Field<string>("song_id"),
row["song_id"].ToString(), db.Field<string>("song_name"),
row["song_name"].ToString(), db.Field<string>("artistA"),
row["artistA"].ToString(), db.Field<string>("artistB"),
row["artistB"].ToString(), db.Field<string>("song_filename"),
row["song_filename"].ToString(), db.Field<string>("artistA_simplified"),
row["artistA_simplified"].ToString(), db.Field<string>("artistB_simplified"),
row["artistB_simplified"].ToString(), db.Field<string>("song_simplified"),
row["song_simplified"].ToString(), db.Field<int>("vocal")
Convert.ToInt32(row["vocal"]) ));
)); }
} }
Console.WriteLine($"查詢到 {searchResults.Count} 筆資料。"); Console.WriteLine($"查詢到 {searchResults.Count} 筆資料。");
} }

View File

@ -44,9 +44,9 @@ namespace DBObj
} }
private static SongData NextPublicSong() private static SongData NextPublicSong()
{ {
if (Room.IsClose()) { if (Program.room.IsClose()) {
publicPlaying = close; publicPlaying = close;
} else if(Room.IsOpen() && isWelcome){ } else if(Program.room.IsOpen() && isWelcome){
isWelcome = false; isWelcome = false;
publicPlaying = welcome; publicPlaying = welcome;
} else { } else {

View File

@ -19,7 +19,7 @@ namespace DualScreenDemo
private static string _localIP = GetLocalIPAddress(); private static string _localIP = GetLocalIPAddress();
private static int _port = 9090; // 或其他方式設置 private static int _port = 9090; // 或其他方式設置
// 服务器类变量 // 服务器类变量
private static SongListManager songListManager; private static SQLManager songListManager;
// 使用完整命名空间来避免歧义 // 使用完整命名空间来避免歧义
public static event ActionString OnDisplayBarrage; public static event ActionString OnDisplayBarrage;
private static DateTime lastClickTime = DateTime.MinValue; private static DateTime lastClickTime = DateTime.MinValue;
@ -35,7 +35,7 @@ namespace DualScreenDemo
private static TaskCompletionSource<string> _qrReadyTcs; private static TaskCompletionSource<string> _qrReadyTcs;
public static async Task StartServer(string baseDirectory, int port, SongListManager manager, CancellationToken token) public static async Task StartServer(string baseDirectory, int port, SQLManager manager, CancellationToken token)
{ {
songListManager = manager; songListManager = manager;
string randomFolderName = CreateRandomFolderAndRedirectHTML(baseDirectory); string randomFolderName = CreateRandomFolderAndRedirectHTML(baseDirectory);

View File

@ -264,26 +264,10 @@ namespace DualScreenDemo
artist.Name; artist.Name;
artistLabel.Text = artistText; artistLabel.Text = artistText;
artistLabel.Tag = artist; artistLabel.Tag = artist;
artistLabel.AutoSize = false; artistLabel.AutoSize = true;
// 計算文字寬度 // 計算文字寬度
Font normalFont = new Font("微軟正黑體", 24, FontStyle.Bold); artistLabel.Font = new Font("微軟正黑體", 20, FontStyle.Bold);
Font mediumFont = new Font("微軟正黑體", 18, FontStyle.Bold);
Font smallFont = new Font("微軟正黑體", 14, FontStyle.Bold);
// 根據文字長度設置字體大小
if (artistLabel.Text.Length > 18)
{
artistLabel.Font = smallFont;
}
else if (artistLabel.Text.Length > 13)
{
artistLabel.Font = mediumFont;
}
else
{
artistLabel.Font = normalFont;
}
Screen screen = Screen.PrimaryScreen; Screen screen = Screen.PrimaryScreen;
int screenWidth = screen.Bounds.Width; int screenWidth = screen.Bounds.Width;
int screenHeight = screen.Bounds.Height; int screenHeight = screen.Bounds.Height;
@ -400,25 +384,10 @@ namespace DualScreenDemo
int textLength = fullText.Length; int textLength = fullText.Length;
// 計算文字寬度 // 計算文字寬度
Font normalFont = new Font("微軟正黑體", 20, FontStyle.Bold); songLabel.Font = new Font("微軟正黑體", 20, FontStyle.Bold);
Font mediumFont = new Font("微軟正黑體", 14, FontStyle.Bold);
Font smallFont = new Font("微軟正黑體", 12, FontStyle.Bold);
// 根據文字長度設置字體大小
if (textLength > 18)
{
songLabel.Font = smallFont;
}
else if (textLength > 8)
{
songLabel.Font = mediumFont;
}
else
{
songLabel.Font = normalFont;
}
songLabel.Text = fullText; songLabel.Text = fullText;
songLabel.Tag = song; songLabel.Tag = song;
songLabel.AutoSize = false; songLabel.AutoSize = true;
// 創建歌手標籤 // 創建歌手標籤
Label artistLabel = new Label(); Label artistLabel = new Label();
artistLabel.Text = artistText; artistLabel.Text = artistText;
@ -457,7 +426,7 @@ namespace DualScreenDemo
{ {
Image = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\其他符號_人聲\其他符號_人聲.png")), Image = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\其他符號_人聲\其他符號_人聲.png")),
SizeMode = PictureBoxSizeMode.Zoom, SizeMode = PictureBoxSizeMode.Zoom,
Size = new Size(32, 32), Size = new Size(30, 30),
Location = new Point(songX + 5, y + 8) Location = new Point(songX + 5, y + 8)
}; };
this.Controls.Add(icon); this.Controls.Add(icon);

View File

@ -2235,7 +2235,7 @@ namespace DualScreenDemo
{ {
HotPlayButton_Click(null, EventArgs.Empty); HotPlayButton_Click(null, EventArgs.Empty);
} }
if (Room.IsClose()) if (Program.room.IsClose())
{ {
ShowSendOffScreen(); ShowSendOffScreen();
} }

View File

@ -7,7 +7,7 @@ namespace DualScreenDemo
public static class Program public static class Program
{ {
// 定义全局变量 // 定义全局变量
internal static DBObj.SongListManager songListManager; internal static DBObj.SQLManager songListManager;
//internal static ArtistManager artistManager; //internal static ArtistManager artistManager;
internal static SerialPortManager serialPortManager; internal static SerialPortManager serialPortManager;
private static PrimaryForm primaryForm; // 儲存實例的參考 private static PrimaryForm primaryForm; // 儲存實例的參考
@ -17,7 +17,7 @@ namespace DualScreenDemo
[STAThread] [STAThread]
static void Main() static void Main()
{ {
Console.WriteLine("Server V.1.2.3 202507211828"); Console.WriteLine("Server V.1.2.4 202507281600");
if (Utils.Env.GetBool("IsCursor", true)) Cursor.Hide(); if (Utils.Env.GetBool("IsCursor", true)) Cursor.Hide();
AppDomain.CurrentDomain.ProcessExit += (s, e) => Cursor.Show(); AppDomain.CurrentDomain.ProcessExit += (s, e) => Cursor.Show();
//Console.WriteLine("正在與中控取得聯繫..."); //Console.WriteLine("正在與中控取得聯繫...");
@ -34,7 +34,7 @@ namespace DualScreenDemo
} }
// 初始化管理器 // 初始化管理器
songListManager = new DBObj.SongListManager(); // 使用单例 songListManager = new DBObj.SQLManager(); // 使用单例
//artistManager = new ArtistManager(); //artistManager = new ArtistManager();
var commandHandler = new CommandHandler(songListManager); var commandHandler = new CommandHandler(songListManager);
@ -49,7 +49,7 @@ namespace DualScreenDemo
} }
// 啟動服務器 // 啟動服務器
var TCP = new TCPServer(); var TCP = new TCPServer(room);
Task.Run(() => HttpServerManager.StartServer()); Task.Run(() => HttpServerManager.StartServer());
// 註冊事件 // 註冊事件

78
Room.cs
View File

@ -1,24 +1,76 @@
using OverlayFormObj; using OverlayFormObj;
using DBObj;
using MySqlConnector;
using System.Data;
namespace DualScreenDemo namespace DualScreenDemo
{ {
public class Room public class Room
{ {
private static string State=Utils.Env.Get("RoomStates", "CLOSE"); public int branch_id =0;
public Room(){} public string hostName;
public static void set(string value)
private string State = "error";
public Room()
{
hostName = System.Net.Dns.GetHostName();
branch_id =getBranchId();
State =getDB();
Console.WriteLine($"hostname status: {hostName},{State}");
}
private int getBranchId()
{
using (var db = new MyDB())
{
string sql = "SELECT id FROM branches LIMIT 1";
if (db.open(sql, null) && db.found())return db.Field<int>("id");
return 0;
}
}
private string getDB()
{
using (var db = new MyDB())
{
string query = @"SELECT * FROM rooms WHERE branch_id = @branch_id AND CONCAT(type, name) = @hostName";
MySqlParameter[] parameters = {
new MySqlParameter("@branch_id", branch_id),
new MySqlParameter("@hostName", hostName)
};
if (db.open(query, parameters) && db.found())
{
State = db.Field<string>("status");
return State;
}
}
return "error";
}
private void setDB(string value)
{
using (var db = new MyDB())
{
string query = @"UPDATE rooms SET status = @status WHERE branch_id = @branch_id AND CONCAT(type, name) = @hostName;";
MySqlParameter[] parameters = {
new MySqlParameter("@status", value),
new MySqlParameter("@branch_id", branch_id),
new MySqlParameter("@hostName", hostName)
};
db.ExecuteNonQuery(query, parameters);
}
}
public void set(string value)
{ {
string marqueeMessage = "歡迎使用超級巨星歡唱,與你共度美好時光。"; string marqueeMessage = "歡迎使用超級巨星歡唱,與你共度美好時光。";
Color c = Color.White; Color c = Color.White;
State = value; setDB(value);
if (value.Equals("PAUSE")) if (value.Equals("fire"))
{ {
PrimaryForm.Instance.ShowSendOffScreen(); PrimaryForm.Instance.ShowSendOffScreen();
VideoPlayerForm.Instance.Pause(); VideoPlayerForm.Instance.Pause();
marqueeMessage = "發生火災,請跟隨引導至逃生出口!!!"; marqueeMessage = "發生火災,請跟隨引導至逃生出口!!!";
c = Color.Red; c = Color.Red;
} }
else if (value.Equals("OPEN")) else if (value.Equals("active"))
{ {
DBObj.SongList.clearSong(); DBObj.SongList.clearSong();
PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty); PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty);
@ -36,19 +88,19 @@ namespace DualScreenDemo
VideoPlayerForm.Instance.PlayNextSong(); VideoPlayerForm.Instance.PlayNextSong();
PrimaryForm.Instance.logout(); PrimaryForm.Instance.logout();
} }
State =getDB();
OverlayForm.MainForm.UpdateMarqueeText(marqueeMessage, OverlayForm.MarqueeStartPosition.Middle, c); OverlayForm.MainForm.UpdateMarqueeText(marqueeMessage, OverlayForm.MarqueeStartPosition.Middle, c);
} }
public static bool IsClose() public bool IsClose()
{ {
return State.Equals("CLOSE"); return State.Equals("closed");
} }
public static bool IsOpen() public bool IsOpen()
{ {
return State.Equals("OPEN"); return State.Equals("active");
} }
} }

View File

@ -12,14 +12,16 @@ namespace DualScreenDemo
private TcpListener listener; private TcpListener listener;
private const int Port = 1000; private const int Port = 1000;
private readonly string hostNameSuffix; private readonly string hostNameSuffix;
private Room _room;
//private bool isProcessingCommand = false; //private bool isProcessingCommand = false;
public TCPServer() public TCPServer(Room room)
{ {
_room = room;
listener = new TcpListener(IPAddress.Any, Port); listener = new TcpListener(IPAddress.Any, Port);
hostNameSuffix = GetHostNameSuffix(); hostNameSuffix = GetHostNameSuffix();
_ = Task.Run(() => Start()); _ = Task.Run(() => Start());
} }
@ -83,10 +85,6 @@ namespace DualScreenDemo
{ {
listener.Start(); listener.Start();
Console.WriteLine("Server started on port " + Port + "."); Console.WriteLine("Server started on port " + Port + ".");
// 心跳封包
//heartbeatSender sender = new heartbeatSender("127.0.0.1", 8888);
//sender.Start();
// Console.WriteLine($"heart beat for server{sender.RemoteEndPoint.Address}:{sender.RemoteEndPoint.Port}");
try { try {
@ -124,7 +122,7 @@ namespace DualScreenDemo
{ {
_ = SafeInvoke(VideoPlayerForm.Instance, async () => _ = SafeInvoke(VideoPlayerForm.Instance, async () =>
{ {
Room.set("CLOSE"); _room.set("closed");
try{ try{
await HttpServer.RestartServer(); await HttpServer.RestartServer();
} catch (Exception ex) { } catch (Exception ex) {
@ -142,7 +140,7 @@ namespace DualScreenDemo
if (command.Trim().Equals("O", StringComparison.OrdinalIgnoreCase)) if (command.Trim().Equals("O", StringComparison.OrdinalIgnoreCase))
{ {
// 開台時跳至首頁 // 開台時跳至首頁
Room.set("OPEN"); _room.set("active");
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n"); byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
stream.Write(okResponse, 0, okResponse.Length); stream.Write(okResponse, 0, okResponse.Length);
continue; continue;
@ -151,7 +149,7 @@ namespace DualScreenDemo
{ {
_ = SafeInvoke(PrimaryForm.Instance, () => _ = SafeInvoke(PrimaryForm.Instance, () =>
{ {
Room.set("PAUSE"); _room.set("fire");
}); });
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n"); byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
stream.Write(okResponse, 0, okResponse.Length); stream.Write(okResponse, 0, okResponse.Length);