DatabaseHelper.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. // 计划(伪代码):
  2. // 1. 在类中新增一个私有方法 `IsSupportedValue(object val)`,用于判断值是否为“常规类型”。
  3. // - 支持类型:null、string、bool、各类数值类型(包括 byte/short/int/long/float/double/decimal 等)、char、DateTime、DateTimeOffset、Guid、枚举。
  4. // - 判定可以使用 Type.IsPrimitive 并单独包含 decimal、string、DateTime 等非 primitive 类型,以及枚举判断。
  5. // 2. 在 `AddCameraRecords` 方法中,遍历 `result` 输出时:
  6. // - 对每个输出取出 name 和 value。
  7. // - 使用 `IsSupportedValue` 判断 value 类型。
  8. // - 如果不支持,跳过该输出(可写日志记录被跳过的输出名和类型)。
  9. // - 如果支持,按原逻辑构造列名并加入 `outputs` 列表。
  10. // 3. 其余逻辑保持不变:
  11. // - 若表不存在则创建,创建列时只为已收集的 `outputs` 创建列。
  12. // - 插入时为 null 写入 DBNull.Value,其他类型统一以 ToString() 写入(保留原行为,以兼容 TEXT 列)。
  13. // 4. 将伪代码以注释形式放在文件顶部,随后是修改后的完整类代码,保证格式与现有代码风格一致,能在 .NET Framework 4.8 / C# 7.3 下编译。
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Data.SQLite;
  17. using System.Data;
  18. using System.Linq;
  19. using System.Text;
  20. using System.Threading.Tasks;
  21. using TeamAAS_VP;
  22. using System.IO;
  23. using TeamAAS_VP.Models;
  24. using TeamAAS_VP.Enums;
  25. using TeamAAS_VP.Core;
  26. using Cognex.VisionPro.ToolBlock;
  27. namespace TeamAAS_VP.Data
  28. {
  29. public class DatabaseHelper
  30. {
  31. /// <summary>
  32. /// 初始化数据库
  33. /// </summary>
  34. /// <returns></returns>
  35. public static bool InitDatabase()
  36. {
  37. try
  38. {
  39. // 保留初始化占位(若需扩展统一建表,可在此实现)
  40. return true;
  41. }
  42. catch (Exception ex)
  43. {
  44. LogHelper.WriteLogError("数据库初始化失败!", ex);
  45. return false;
  46. }
  47. }
  48. // ------------------- 新增:产品相机记录相关方法 -------------------
  49. private static string SanitizeIdentifier(string name)
  50. {
  51. if (string.IsNullOrWhiteSpace(name)) return "unnamed";
  52. var sb = new StringBuilder();
  53. foreach (var ch in name.Trim())
  54. {
  55. if (char.IsLetterOrDigit(ch) || ch == '_')
  56. sb.Append(ch);
  57. else
  58. sb.Append('_');
  59. }
  60. var s = sb.ToString();
  61. while (s.Contains("__")) s = s.Replace("__", "_");
  62. s = s.Trim('_');
  63. if (string.IsNullOrEmpty(s)) s = "unnamed";
  64. if (char.IsDigit(s[0])) s = "a" + s;
  65. if (s.Length > 64) s = s.Substring(0, 64);
  66. return s;
  67. }
  68. private static string SanitizeFileName(string name)
  69. {
  70. if (string.IsNullOrWhiteSpace(name)) return "unnamed";
  71. var invalid = Path.GetInvalidFileNameChars();
  72. var sb = new StringBuilder();
  73. foreach (var ch in name.Trim())
  74. {
  75. if (invalid.Contains(ch))
  76. sb.Append('_');
  77. else
  78. sb.Append(ch);
  79. }
  80. var s = sb.ToString();
  81. if (char.IsDigit(s[0])) s = "a" + s;
  82. s = s.Replace(" ", "_");
  83. if (s.Length > 100) s = s.Substring(0, 100);
  84. return s;
  85. }
  86. private static string GetProductDbPath(string productName)
  87. {
  88. var safeProduct = SanitizeFileName(productName);
  89. var folder = Path.Combine(FilePath.ProductsPath, productName);
  90. Directory.CreateDirectory(folder);
  91. var dbFileName = safeProduct + ".db";
  92. return Path.Combine(folder, dbFileName);
  93. }
  94. /// <summary>
  95. /// 增加一条相机拍照记录(若表或列不存在则创建)
  96. /// </summary>
  97. /// <param name="ProductName"></param>
  98. /// <param name="ProcedureName"></param>
  99. /// <param name="result"></param>
  100. public static void AddCameraRecords(string ProductName, string ProcedureName, CogToolBlockTerminalCollection result)
  101. {
  102. try
  103. {
  104. if (string.IsNullOrWhiteSpace(ProductName) || string.IsNullOrWhiteSpace(ProcedureName) || result == null)
  105. {
  106. return;
  107. }
  108. var dbpath = GetProductDbPath(ProductName);
  109. if (!File.Exists(dbpath))
  110. {
  111. SQLiteConnection.CreateFile(dbpath);
  112. }
  113. var tableName = SanitizeIdentifier(ProcedureName);
  114. var outputs = new List<(string Col, object Value)>();
  115. foreach (CogToolBlockTerminal output in result)
  116. {
  117. var col = "_" + SanitizeIdentifier(output.Name ?? "unnamed");
  118. outputs.Add((col, output.Value));
  119. }
  120. if (!IsHaveTable(tableName, dbpath))
  121. {
  122. var colsSb = new StringBuilder();
  123. colsSb.Append("_Time DATETIME");
  124. foreach (var o in outputs)
  125. {
  126. colsSb.Append($", {o.Col} TEXT");
  127. }
  128. CreateTable(tableName, $"({colsSb})", dbpath);
  129. }
  130. else
  131. {
  132. foreach (var o in outputs)
  133. {
  134. if (!IsHaveColumn(o.Col, tableName, dbpath))
  135. {
  136. AddColumn(o.Col, "TEXT", tableName, dbpath);
  137. }
  138. }
  139. if (!IsHaveColumn("_Time", tableName, dbpath))
  140. {
  141. AddColumn("_Time", "DATETIME", tableName, dbpath);
  142. }
  143. }
  144. using (var conn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbpath)))
  145. {
  146. conn.Open();
  147. using (var cmd = new SQLiteCommand(conn))
  148. {
  149. var colNames = new List<string> { "_Time" };
  150. var paramNames = new List<string> { "@p_time" };
  151. cmd.Parameters.AddWithValue("@p_time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
  152. for (int i = 0; i < outputs.Count; i++)
  153. {
  154. var pName = $"@p{i}";
  155. colNames.Add($"\"{outputs[i].Col}\"");
  156. var val = outputs[i].Value;
  157. if (val == null) cmd.Parameters.AddWithValue(pName, DBNull.Value);
  158. else cmd.Parameters.AddWithValue(pName, val.ToString());
  159. paramNames.Add(pName);
  160. }
  161. var sql = $"INSERT INTO \"{tableName}\" ({string.Join(", ", colNames)}) VALUES ({string.Join(", ", paramNames)});";
  162. cmd.CommandText = sql;
  163. cmd.ExecuteNonQuery();
  164. }
  165. }
  166. }
  167. catch (Exception ex)
  168. {
  169. LogHelper.WriteLogError("增加一条相机拍照记录至数据库时出错!", ex);
  170. }
  171. }
  172. /// <summary>
  173. /// 获取最近20条相机拍照记录(按时间倒序)
  174. /// </summary>
  175. /// <param name="ProductName">产品名</param>
  176. /// <param name="ProcedureName">工序名</param>
  177. /// <returns>最近20条记录集合</returns>
  178. public static List<Dictionary<string, object>> GetLast20CameraRecords(string ProductName, string ProcedureName)
  179. {
  180. var records = new List<Dictionary<string, object>>();
  181. try
  182. {
  183. var dbpath = GetProductDbPath(ProductName);
  184. if (!File.Exists(dbpath))
  185. return records;
  186. var tableName = SanitizeIdentifier(ProcedureName);
  187. if (!IsHaveTable(tableName, dbpath))
  188. return records;
  189. using (var conn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbpath)))
  190. {
  191. conn.Open();
  192. using (var cmd = new SQLiteCommand(conn))
  193. {
  194. // 按时间倒序取最新20条
  195. cmd.CommandText = $"SELECT * FROM \"{tableName}\" ORDER BY _Time DESC LIMIT 20;";
  196. using (var reader = cmd.ExecuteReader())
  197. {
  198. while (reader.Read())
  199. {
  200. var row = new Dictionary<string, object>();
  201. for (int i = 0; i < reader.FieldCount; i++)
  202. {
  203. var colName = reader.GetName(i);
  204. var value = reader.IsDBNull(i) ? null : reader.GetValue(i);
  205. row[colName] = value;
  206. }
  207. records.Add(row);
  208. }
  209. }
  210. }
  211. }
  212. }
  213. catch (Exception ex)
  214. {
  215. LogHelper.WriteLogError("获取最近20条相机拍照记录时出错!", ex);
  216. }
  217. return records;
  218. }
  219. /// <summary>
  220. /// 根据产品名、流程名与可选时间段查询相机记录,返回 DataTable(表不存在时返回空表)
  221. /// </summary>
  222. public static DataTable GetCameraRecords(string ProductName, string ProcedureName, DateTime? from = null, DateTime? to = null)
  223. {
  224. var table = new DataTable();
  225. try
  226. {
  227. if (string.IsNullOrWhiteSpace(ProductName) || string.IsNullOrWhiteSpace(ProcedureName))
  228. return table;
  229. var dbpath = GetProductDbPath(ProductName);
  230. var tableName = SanitizeIdentifier(ProcedureName);
  231. if (!File.Exists(dbpath) || !IsHaveTable(tableName, dbpath))
  232. return table;
  233. var sb = new StringBuilder();
  234. sb.Append($"SELECT * FROM \"{tableName}\"");
  235. var parameters = new List<SQLiteParameter>();
  236. if (from.HasValue || to.HasValue)
  237. {
  238. var where = new List<string>();
  239. if (from.HasValue)
  240. {
  241. where.Add("_Time >= @from");
  242. parameters.Add(new SQLiteParameter("@from", from.Value.ToString("yyyy-MM-dd HH:mm:ss")));
  243. }
  244. if (to.HasValue)
  245. {
  246. where.Add("_Time <= @to");
  247. parameters.Add(new SQLiteParameter("@to", to.Value.ToString("yyyy-MM-dd HH:mm:ss")));
  248. }
  249. if (where.Any())
  250. {
  251. sb.Append(" WHERE " + string.Join(" AND ", where));
  252. }
  253. }
  254. sb.Append(" ORDER BY _Time DESC");
  255. using (var conn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbpath)))
  256. {
  257. conn.Open();
  258. using (var cmd = new SQLiteCommand(sb.ToString(), conn))
  259. {
  260. if (parameters.Any()) cmd.Parameters.AddRange(parameters.ToArray());
  261. using (var da = new SQLiteDataAdapter(cmd))
  262. {
  263. da.Fill(table);
  264. }
  265. }
  266. }
  267. }
  268. catch (Exception ex)
  269. {
  270. LogHelper.WriteLogError("查询相机拍照记录时出错!", ex);
  271. }
  272. return table;
  273. }
  274. /// <summary>
  275. /// 根据产品名、流程名与可选时间段查询相机记录,返回 DataTable(表不存在时返回空表)
  276. /// </summary>
  277. /// <param name="ProductName"></param>
  278. /// <param name="ProcedureName"></param>
  279. /// <param name="from"></param>
  280. /// <param name="to"></param>
  281. /// <returns></returns>
  282. public static Task<DataTable> GetCameraRecordsAsync(string ProductName, string ProcedureName, DateTime? from = null, DateTime? to = null)
  283. {
  284. return Task.Run(() => GetCameraRecords(ProductName, ProcedureName, from, to));
  285. }
  286. // ------------------- 结束新增 -------------------
  287. /// <summary>
  288. /// 检测数据库库中是否存在表
  289. /// </summary>
  290. /// <param name="TableName">表名称</param>
  291. public static bool IsHaveTable(string TableName, string dbpath)
  292. {
  293. try
  294. {
  295. using (var conn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbpath)))
  296. {
  297. if (conn.State != ConnectionState.Open) conn.Open();
  298. string cmdStr = $"select COUNT(*) from sqlite_master where type='table' and name ='{TableName}' ";
  299. using (var cmd = new SQLiteCommand(cmdStr, conn))
  300. {
  301. return Convert.ToInt32(cmd.ExecuteScalar()) > 0;
  302. }
  303. }
  304. }
  305. catch (Exception ex)
  306. {
  307. LogHelper.WriteLogError($"检测数据库库中是否存在表({TableName})时出错", ex);
  308. return false;
  309. }
  310. }
  311. /// <summary>
  312. /// 为数据创建表
  313. /// </summary>
  314. /// <param name="TableName">表的名称</param>
  315. /// <param name="column">列的名称和类型字符串(Name varchar,Team varchar, Number varchar)</param>
  316. public static bool CreateTable(string TableName, string column, string dbpath)
  317. {
  318. string query = "CREATE TABLE " + TableName + " " + column;
  319. return WriteCommandToDatabase(query, dbpath);
  320. }
  321. /// <summary>
  322. /// 为表增加列
  323. /// </summary>
  324. /// <param name="ColumnName"></param>
  325. /// <param name="ColumnType"></param>
  326. /// <param name="TableName"></param>
  327. /// <param name="dbpath"></param>
  328. public static void AddColumn(string ColumnName, string ColumnType, string TableName, string dbpath)
  329. {
  330. using (SQLiteConnection connection = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbpath)))
  331. {
  332. connection.Open();
  333. string query = $"ALTER TABLE {TableName} ADD COLUMN {ColumnName} {ColumnType}";
  334. using (SQLiteCommand command = new SQLiteCommand(query, connection))
  335. {
  336. command.ExecuteNonQuery();
  337. }
  338. }
  339. }
  340. /// <summary>
  341. /// 将命令写入数据库
  342. /// </summary>
  343. /// <param name="cmdstr"></param>
  344. public static bool WriteCommandToDatabase(string cmdstr, string dbpath)
  345. {
  346. try
  347. {
  348. using (var conn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbpath)))
  349. {
  350. if (conn.State != ConnectionState.Open) conn.Open();
  351. using (var cmd = new SQLiteCommand(cmdstr, conn))
  352. {
  353. cmd.ExecuteNonQuery();
  354. }
  355. }
  356. return true;
  357. }
  358. catch (Exception ex)
  359. {
  360. LogHelper.WriteLogError($"写入命令至数据库时出错:【{cmdstr}】", ex);
  361. return false;
  362. }
  363. }
  364. /// <summary>
  365. /// 检索数据
  366. /// </summary>
  367. /// <param name="strCmd"></param>
  368. /// <param name="tab"></param>
  369. /// <returns></returns>
  370. public static bool Select(string strCmd, ref DataTable tab, string dbPath)
  371. {
  372. try
  373. {
  374. using (var conn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbPath)))
  375. {
  376. if (conn.State != ConnectionState.Open) conn.Open();
  377. using (var cmd = new SQLiteCommand(strCmd, conn))
  378. {
  379. using (var da = new SQLiteDataAdapter(cmd))
  380. {
  381. da.Fill(tab);
  382. }
  383. }
  384. }
  385. return true;
  386. }
  387. catch (Exception ex)
  388. {
  389. LogHelper.WriteLogError("检索数据库时出错!", ex);
  390. return false;
  391. }
  392. }
  393. /// <summary>
  394. /// 检查是否存在包含特定字符串的记录
  395. /// </summary>
  396. /// <param name="TableName"></param>
  397. /// <param name="ColumnName"></param>
  398. /// <param name="searchString"></param>
  399. /// <returns></returns>
  400. public static bool RecordExists(string TableName, string ColumnName, string searchString, string dbPath)
  401. {
  402. try
  403. {
  404. using (var conn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbPath)))
  405. {
  406. if (conn.State != ConnectionState.Open) conn.Open();
  407. using (var command = new SQLiteCommand($"SELECT COUNT(*) FROM {TableName} WHERE {ColumnName} LIKE @pattern", conn))
  408. {
  409. command.Parameters.AddWithValue("@pattern", $"%{searchString}%");
  410. var obj = command.ExecuteScalar();
  411. if (obj == null) return false;
  412. long count = Convert.ToInt64(obj);
  413. return count > 0;
  414. }
  415. }
  416. }
  417. catch (Exception ex)
  418. {
  419. LogHelper.WriteLogError($"检查是否存在包含特定字符串的记录时出错", ex);
  420. return false;
  421. }
  422. }
  423. /// <summary>
  424. /// 检测表中是否存在列
  425. /// </summary>
  426. /// <param name="columnName"></param>
  427. /// <param name="TableName"></param>
  428. /// <param name="dbpath"></param>
  429. /// <returns></returns>
  430. public static bool IsHaveColumn(string columnName, string TableName, string dbpath)
  431. {
  432. try
  433. {
  434. using (SQLiteConnection connection = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbpath)))
  435. {
  436. connection.Open();
  437. string query = $"PRAGMA table_info({TableName});";
  438. using (SQLiteCommand command = new SQLiteCommand(query, connection))
  439. {
  440. using (SQLiteDataReader reader = command.ExecuteReader())
  441. {
  442. while (reader.Read())
  443. {
  444. string columnNameFromDB = reader["name"].ToString();
  445. if (columnNameFromDB == columnName)
  446. {
  447. return true;
  448. }
  449. }
  450. }
  451. }
  452. }
  453. return false;
  454. }
  455. catch (Exception)
  456. {
  457. return false;
  458. }
  459. }
  460. }
  461. }