Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

413 linhas
16KB

  1. extern alias CustomTypes;
  2. using HealthMonitor.Common;
  3. using HealthMonitor.Common.helper;
  4. using HealthMonitor.Model.Config;
  5. using HealthMonitor.Service.Biz.db.Dto;
  6. using Microsoft.EntityFrameworkCore.Metadata.Internal;
  7. using Microsoft.Extensions.Logging;
  8. using Microsoft.Extensions.Options;
  9. using Newtonsoft.Json;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Threading.Tasks;
  15. using CustomTypes.TDengineDriver;
  16. using CustomTypes.TDengineDriver.Impl;
  17. namespace HealthMonitor.Service.Biz.db
  18. {
  19. public class TDengineService
  20. {
  21. private readonly ILogger<TDengineService> _logger;
  22. private readonly HttpHelper _httpHelper=default!;
  23. private readonly TDengineServiceConfig _configTDengineService;
  24. public TDengineService(ILogger<TDengineService> logger,
  25. IOptions<TDengineServiceConfig> configTDengineService,
  26. HttpHelper httpHelper
  27. )
  28. {
  29. _logger = logger;
  30. _configTDengineService = configTDengineService.Value;
  31. _httpHelper = httpHelper;
  32. }
  33. public IntPtr Connection()
  34. {
  35. string host = _configTDengineService.Host;
  36. string user = _configTDengineService.UserName;
  37. string db = _configTDengineService.DB;
  38. short port = _configTDengineService.Port;
  39. string password = _configTDengineService.Password;
  40. //#if DEBUG
  41. // //string configDir = "C:/TDengine/cfg";
  42. // //TDengine.Options((int)TDengineInitOption.TSDB_OPTION_CONFIGDIR, configDir);
  43. // TDengine.Options((int)TDengineInitOption.TSDB_OPTION_TIMEZONE, "Asia/Beijing");
  44. //#endif
  45. IntPtr conn = TDengineDriver.TDengine.Connect(host, user, password, db, port);
  46. if (conn == IntPtr.Zero)
  47. {
  48. _logger.LogError($"连接 TDengine 失败....");
  49. }
  50. else
  51. {
  52. _logger.LogInformation($"连接 TDengine 成功....");
  53. }
  54. return conn;
  55. }
  56. public void ExecuteSQL(IntPtr conn, string sql)
  57. {
  58. IntPtr res = TDengine.Query(conn, sql);
  59. // Check if query success
  60. if ((res == IntPtr.Zero) || (TDengine.ErrorNo(res) != 0))
  61. {
  62. Console.Write(sql + " failure, ");
  63. // Get error message while Res is a not null pointer.
  64. if (res != IntPtr.Zero)
  65. {
  66. Console.Write("reason:" + TDengine.Error(res));
  67. }
  68. }
  69. else
  70. {
  71. Console.Write(sql + " success, {0} rows affected", TDengine.AffectRows(res));
  72. //... do something with res ...
  73. // Important: need to free result to avoid memory leak.
  74. TDengine.FreeResult(res);
  75. }
  76. }
  77. public void ExecuteQuerySQL(IntPtr conn, string sql)
  78. {
  79. IntPtr res = TDengine.Query(conn, sql);
  80. // Check if query success
  81. if ((res == IntPtr.Zero) || (TDengine.ErrorNo(res) != 0))
  82. {
  83. Console.Write(sql + " failure, ");
  84. // Get error message while Res is a not null pointer.
  85. if (res != IntPtr.Zero)
  86. {
  87. Console.Write("reason:" + TDengine.Error(res));
  88. }
  89. }
  90. else
  91. {
  92. Console.Write(sql + " success, {0} rows affected", TDengine.AffectRows(res));
  93. //... do something with res ...
  94. List<TDengineMeta> resMeta = LibTaos.GetMeta(res);
  95. List<object> resData = LibTaos.GetData(res);
  96. foreach (var meta in resMeta)
  97. {
  98. _logger.LogInformation("\t|{meta.name} {meta.TypeName()} ({meta.size})\t|", meta.name, meta.TypeName(), meta.size);
  99. }
  100. for (int i = 0; i < resData.Count; i++)
  101. {
  102. _logger.LogInformation($"|{resData[i].ToString()} \t");
  103. if (((i + 1) % resMeta.Count == 0))
  104. {
  105. _logger.LogInformation("");
  106. }
  107. }
  108. // Important: need to free result to avoid memory leak.
  109. TDengine.FreeResult(res);
  110. }
  111. }
  112. public void CheckRes(IntPtr conn, IntPtr res, String errorMsg)
  113. {
  114. if (TDengine.ErrorNo(res) != 0)
  115. {
  116. throw new Exception($"{errorMsg} since: {TDengine.Error(res)}");
  117. }
  118. }
  119. public void ExecuteInsertSQL(string sql)
  120. {
  121. var conn = Connection();
  122. try
  123. {
  124. //sql = "INSERT INTO d1001 USING meters TAGS('California.SanFrancisco', 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) " +
  125. // "d1002 USING power.meters TAGS('California.SanFrancisco', 3) VALUES('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) " +
  126. // "d1003 USING power.meters TAGS('California.LosAngeles', 2) VALUES('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000)('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) " +
  127. // "d1004 USING power.meters TAGS('California.LosAngeles', 3) VALUES('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000)('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)";
  128. //#if DEBUG
  129. // //string configDir = "C:/TDengine/cfg";
  130. // //TDengine.Options((int)TDengineInitOption.TSDB_OPTION_CONFIGDIR, configDir);
  131. // TDengine.Options((int)TDengineInitOption.TSDB_OPTION_TIMEZONE, "Asia/Beijing");
  132. //#endif
  133. IntPtr res = TDengine.Query(conn, sql);
  134. CheckRes(conn, res, "failed to insert data");
  135. int affectedRows = TDengine.AffectRows(res);
  136. _logger.LogInformation("affectedRows {affectedRows}" , affectedRows);
  137. TDengine.FreeResult(res);
  138. }
  139. finally
  140. {
  141. TDengine.Close(conn);
  142. }
  143. }
  144. #region TDengine.Connector async query
  145. public void QueryCallback(IntPtr param, IntPtr taosRes, int code)
  146. {
  147. if (code == 0 && taosRes != IntPtr.Zero)
  148. {
  149. FetchRawBlockAsyncCallback fetchRowAsyncCallback = new FetchRawBlockAsyncCallback(FetchRawBlockCallback);
  150. TDengine.FetchRawBlockAsync(taosRes, fetchRowAsyncCallback, param);
  151. }
  152. else
  153. {
  154. _logger.LogInformation("async query data failed, failed code {code}",code);
  155. }
  156. }
  157. // Iteratively call this interface until "numOfRows" is no greater than 0.
  158. public void FetchRawBlockCallback(IntPtr param, IntPtr taosRes, int numOfRows)
  159. {
  160. if (numOfRows > 0)
  161. {
  162. _logger.LogInformation("{numOfRows} rows async retrieved", numOfRows);
  163. IntPtr pdata = TDengine.GetRawBlock(taosRes);
  164. List<TDengineMeta> metaList = TDengine.FetchFields(taosRes);
  165. List<object> dataList = LibTaos.ReadRawBlock(pdata, metaList, numOfRows);
  166. for (int i = 0; i < metaList.Count; i++)
  167. {
  168. _logger.LogInformation("{0} {1}({2}) \t|", metaList[i].name, metaList[i].type, metaList[i].size);
  169. }
  170. _logger.LogInformation("");
  171. for (int i = 0; i < dataList.Count; i++)
  172. {
  173. if (i != 0 && i % metaList.Count == 0)
  174. {
  175. _logger.LogInformation("{dataList[i]}\t|", dataList[i]);
  176. }
  177. _logger.LogInformation("{dataList[i]}\t|", dataList[i]);
  178. }
  179. TDengine.FetchRawBlockAsync(taosRes, FetchRawBlockCallback, param);
  180. }
  181. else
  182. {
  183. if (numOfRows == 0)
  184. {
  185. _logger.LogInformation("async retrieve complete.");
  186. }
  187. else
  188. {
  189. _logger.LogInformation("FetchRawBlockCallback callback error, error code {numOfRows}", numOfRows);
  190. }
  191. TDengine.FreeResult(taosRes);
  192. }
  193. }
  194. public void ExecuteQueryAsync(string sql)
  195. {
  196. var conn = Connection();
  197. QueryAsyncCallback queryAsyncCallback = new QueryAsyncCallback(QueryCallback);
  198. CustomTypes.TDengineDriver.TDengine.QueryAsync(conn, sql, queryAsyncCallback, IntPtr.Zero);
  199. }
  200. //public void ExecuteQuery(string sql)
  201. //{
  202. // var conn = Connection();
  203. // QueryAsyncCallback queryAsyncCallback = new QueryAsyncCallback(QueryCallback);
  204. // TDengine.QueryAsync(conn, sql, queryAsyncCallback, IntPtr.Zero);
  205. //}
  206. public Aggregate GetAggregateValue(string field, string tbName, string? condition)
  207. {
  208. List<int> data = new();
  209. var sql = $"SELECT MAX({field}), MIN({field}) FROM {_configTDengineService.DB}.{tbName} WHERE {condition}";
  210. var conn = Connection();
  211. try
  212. {
  213. IntPtr res = TDengine.Query(conn, sql);
  214. // Check if query success
  215. if ((res == IntPtr.Zero) || (TDengine.ErrorNo(res) != 0))
  216. {
  217. Console.Write(sql + " failure, ");
  218. // Get error message while Res is a not null pointer.
  219. if (res != IntPtr.Zero)
  220. {
  221. Console.Write("reason:" + TDengine.Error(res));
  222. }
  223. }
  224. else
  225. {
  226. Console.Write(sql + " success, {0} rows affected", TDengine.AffectRows(res));
  227. //... do something with res ...
  228. List<TDengineMeta> resMeta = LibTaos.GetMeta(res);
  229. List<object> resData = LibTaos.GetData(res);
  230. foreach (var meta in resMeta)
  231. {
  232. Console.Write($"\t|{meta.name} {meta.TypeName()} ({meta.size})\t|");
  233. }
  234. resData.ForEach(x => data.Add(SafeType.SafeInt(x)));
  235. // Important: need to free result to avoid memory leak.
  236. TDengine.FreeResult(res);
  237. }
  238. }
  239. finally
  240. {
  241. TDengine.Close(conn);
  242. }
  243. return new Aggregate
  244. {
  245. Max = data.Count.Equals(0) ? 0 : data[0],
  246. Min = data.Count.Equals(0) ? 0 : data[1],
  247. };
  248. }
  249. public int GetAvgExceptMaxMinValue(string field, string tbName, string? condition)
  250. {
  251. List<int> data = new();
  252. var sql = $"SELECT MAX({field}), MIN({field}) FROM {_configTDengineService.DB}.{tbName} WHERE {condition}";
  253. var aggregate= GetAggregateValue(field, tbName, condition);
  254. var sqlAvg = $"SELECT AVG({field}) FROM {_configTDengineService.DB}.{tbName} WHERE {condition} AND {field} < {aggregate.Max} and {field} > {aggregate.Min}";
  255. var conn = Connection();
  256. try
  257. {
  258. IntPtr res = TDengine.Query(conn, sqlAvg);
  259. // Check if query success
  260. if ((res == IntPtr.Zero) || (TDengine.ErrorNo(res) != 0))
  261. {
  262. Console.Write(sqlAvg + " failure, ");
  263. // Get error message while Res is a not null pointer.
  264. if (res != IntPtr.Zero)
  265. {
  266. Console.Write("reason:" + TDengine.Error(res));
  267. }
  268. }
  269. else
  270. {
  271. Console.Write(sqlAvg + " success, {0} rows affected", TDengine.AffectRows(res));
  272. //... do something with res ...
  273. List<TDengineMeta> resMeta = LibTaos.GetMeta(res);
  274. List<object> resData = LibTaos.GetData(res);
  275. foreach (var meta in resMeta)
  276. {
  277. Console.Write($"\t|{meta.name} {meta.TypeName()} ({meta.size})\t|");
  278. }
  279. resData.ForEach(x => data.Add(SafeType.SafeInt(x)));
  280. // Important: need to free result to avoid memory leak.
  281. TDengine.FreeResult(res);
  282. }
  283. }
  284. finally
  285. {
  286. TDengine.Close(conn);
  287. }
  288. return data.Count.Equals(0) ? 0 : data[0];
  289. }
  290. #endregion
  291. #region RestAPI
  292. public async Task<bool> GernalRestSql(string sql)
  293. {
  294. //"http://{server}:{port}/rest/sql/{db}"
  295. var url = $"http://{_configTDengineService.Host}:{_configTDengineService.RestPort}/rest/sql/{_configTDengineService.DB}";
  296. List<KeyValuePair<string, string>> headers = new()
  297. {
  298. new KeyValuePair<string, string>("Authorization", "Basic " + _configTDengineService.Token)
  299. };
  300. var result = await _httpHelper.HttpToPostAsync(url, sql, headers).ConfigureAwait(false);
  301. var res = JsonConvert.DeserializeObject<TDengineRestResBase>(result!);
  302. if (result != null)
  303. {
  304. if (res?.Code == 0)
  305. {
  306. _logger.LogInformation($"{nameof(GernalRestSql)},SQL 语句执行成功|{sql}");
  307. return true;
  308. }
  309. else
  310. {
  311. _logger.LogWarning($"{nameof(GernalRestSql)},SQL 语句执行失败||{sql}");
  312. return false;
  313. }
  314. }
  315. else
  316. {
  317. _logger.LogError($"{nameof(GernalRestSql)},TDengine 服务器IP:{_configTDengineService.Host} 错误,请联系运维人员");
  318. return false;
  319. }
  320. //return res.Code==0;
  321. }
  322. public async Task<string?> GernalRestSqlResTextAsync(string sql)
  323. {
  324. var url = $"http://{_configTDengineService.Host}:{_configTDengineService.RestPort}/rest/sql/{_configTDengineService.DB}";
  325. List<KeyValuePair<string, string>> headers = new()
  326. {
  327. new KeyValuePair<string, string>("Authorization", "Basic " + _configTDengineService.Token)
  328. };
  329. var result = await _httpHelper.HttpToPostAsync(url, sql, headers).ConfigureAwait(false);
  330. return result;
  331. }
  332. public async Task<Aggregate> GetAggregateValueAsync(string field,string tbName,string? condition)
  333. {
  334. var sql = $"SELECT MAX({field}), MIN({field}) FROM {_configTDengineService.DB}.{tbName} WHERE {condition}";
  335. var result = await GernalRestSqlResTextAsync(sql);
  336. var res = JsonConvert.DeserializeObject<Aggregate>(result!);
  337. List<dynamic> data = res?.Data!;
  338. return new Aggregate
  339. {
  340. Max = data.Count.Equals(0) ? 0 : data[0][0],
  341. Min = data.Count.Equals(0) ? 0 : data[0][1],
  342. };
  343. }
  344. public async Task<int> GetAvgExceptMaxMinValueAsync(string field, string tbName, string? condition)
  345. {
  346. var sql = $"SELECT MAX({field}), MIN({field}) FROM {_configTDengineService.DB}.{tbName} WHERE {condition}";
  347. var result = await GernalRestSqlResTextAsync(sql);
  348. var res = JsonConvert.DeserializeObject<TDengineRestResBase>(result!);
  349. List<dynamic> data = res?.Data!;
  350. var sqlAvg = $"SELECT AVG({field}) FROM {_configTDengineService.DB}.{tbName} WHERE {condition} AND {field} < { (data.Count.Equals(0)? 0: data[0][0]) } and {field} > {(data.Count.Equals(0) ? 0 : data[0][1])}";
  351. result = await GernalRestSqlResTextAsync(sqlAvg);
  352. res = JsonConvert.DeserializeObject<TDengineRestResBase>(result!);
  353. data = res?.Data!;
  354. return data.Count.Equals(0)?0:(int)data[0][0];
  355. }
  356. #endregion
  357. }
  358. }