AI+C#+过温预警分析

十年河东,十年河西,莫欺少年穷

using AliyunHelper.AliyunOts;
using Aop.Api.Domain;
using CoreDbContext;
using CoreDbContext.Context;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OpenAI;
using OpenAI.Chat;
using PgSqlPublicContext.Context;
using swapCommon;
using swapCommon.Enums;
using swapModels.ChargeService;
using swapModels.EvlDtos;
using System.ClientModel;
using System.ComponentModel;
using System.Text;
using System.Text.Json;
using static AliyunHelper.AliyunOts.OtsDeviceAlmTxsn;
using Microsoft.Extensions.AI.Mcp;


namespace OpenClawClient
{

    class Program
    {  
        static async Task Main()
        {
            ConfigurationBuilder builder = new ConfigurationBuilder();
            builder.AddJsonFile("appsettings.json", true, true);
            var ConfigRoot = builder.Build();//根节点
            IServiceCollection Services = new ServiceCollection();
            Services.AddDbContext<swapDbContext>(options => options.UseSqlServer(ConfigRoot.GetConnectionString("swapDbContext")), ServiceLifetime.Transient);
            //
            Services.AddLogging(log => { log.AddConsole(); log.SetMinimumLevel(LogLevel.Error); });
            Services.AddOptions().Configure<openAiOptions>(A => ConfigRoot.GetSection("openAiOptions").Bind(A));
            IConfigurationRoot config = new ConfigurationBuilder()
                .AddUserSecrets<Program>()
                .Build();

            string model = ConfigurationHelper.GetConfig("openAiOptions:Model");
            string key = ConfigurationHelper.GetConfig("openAiOptions:aiKey");
            string Url = ConfigurationHelper.GetConfig("openAiOptions:Uri");

            var options = new OpenAIClientOptions
            {
                Endpoint = new Uri(Url)
            };
            var credential = new ApiKeyCredential(key);
            IChatClient client = new OpenAIClient(credential, options)
                .GetChatClient(model)
                .AsIChatClient();
            // ========== 调用电池数据 API ==========
            Console.WriteLine("📡 正在获取电池数据...");
            var batteryData = GetBatteryDataAsync();

            if (batteryData == null)
            {
                Console.WriteLine("❌ 获取电池数据失败,退出程序");
                return;
            } 

            string jsonData = JsonSerializer.Serialize(batteryData, new JsonSerializerOptions { WriteIndented = false });
            Console.WriteLine($"✅ 数据大小:{jsonData.Length} 字符");
            //:6886216
            if (jsonData.Length > 500000)
            {
                Console.WriteLine("⚠️ 数据过大..."); return;
            }



            // ========== 构建提示词 ==========
            //{  Createtime=A.Createtime, Ri=A.Ri,   Rv=A.Rv,  Ti=A.Ti, Tt=A.Tt, Tv=A.Tv, Txsn=A.Txsn}
            string userPrompt = $"""
                你是充电设备运营情况分析专家。请根据充电设备充电时间及充电时间前后1小时的充电数据,提取充电设备发生过温的共性。

                ## 提取说明
                - 从结果中提取信息,返回 JSON 格式。
                - 只返回 JSON,不要任何其他文字。
                -JSON 格式:List<T> [("Txsn": "充电设备编号","createtime": "预警时间","pointsPowerMax": "功率差大于60W的points充电数据的数量","pointsPowerMin": "功率差小于等于60W的points充电数据的数量","pointsPowerMax_2": "功率差大于60W的points_2充电数据的数量","pointsPowerMin_2": "功率差小于等于60W的points_2充电数据的数量","minTemp": "points充电数据最小温度", "maxTemp": points充电数据最大温度,"avgTemp": "points充电数据平均温度","minTemp_2": "points_2充电数据最小温度", "maxTemp_2": points_2充电数据最大温度,"avgTemp_2": "points_2充电数据平均温度""TempTime": "points充电数据最小温度上升至最大问题的时间(分钟)""TempTime_2": "points_2充电数据最小温度上升至最大问题的时间(分钟)", "maxPWM": "points充电数据中PWM最大值", "minPWM": "points充电数据中PWM最小值", "avgPWM": "points充电数据中PWM平均值", "maxPWM_2": "points_2充电数据中PWM最大值", "minPWM_2": "points_2充电数据中PWM最小值", "avgPWM_2": "points_2充电数据中PWM平均值")、......], "Info": "你给出的总结"

                ## 充电设备及充电数据结构
                ```json
                {jsonData}
                ```

                ## 充电设备及充电数据结构
                ### 关联充电数据JSON格式: "txsn": "充电设备编号", "createtime": 充电数据采集点(会采集前后各10小时的数据),"points": "本次充电关联的温度介于25-50摄氏度之间的充电数据""points_2": "本次充电关联的温度大于80摄氏度充电数据,温度大于89时触发高温报警"

                ### 本次充电关联的充电数据的JSON格式: "Txsn": "充电设备编号", "Rv": 电池电压, "Ri": "电池电流""Tt": "充电设备的温度""Tv": "充电设备电压""Ti": "充电设备电流""PWM": "PWM值" 

                - 要结合充电数据进行共性分析  
                - 重要:功率差的计算方式为:Ti*Tv-Ri*Rv
                - 重要:minTemp为points[本次充电的关联充电数据]中最小温度
                - 重要:maxTemp为points[本次充电的关联充电数据]中最大温度
                - 重要:avgTemp为points[本次充电的关联充电数据]中平均温度
                - 重要:minTemp_2为points_2[本次充电的关联充电数据]中最小温度
                - 重要:maxTemp_2为points_2[本次充电的关联充电数据]中最大温度
                - 重要:avgTemp_2为points_2[本次充电的关联充电数据]中平均温度
                - 重要:TempTime为points中最小温度上升至points_2最大温度的时间,单位分钟
                - 重要:PWM最大值/最小值/平均值参考[本次充电的关联充电数据]points中的PWM字段进行计算
                - 重要:PWM_2最大值/最小值/平均值参考[本次充电的关联充电数据]points_2中的PWM字段进行计算
                - 最重要:Info字段最重要,你需要根据提供的充电数据,整体分析共性问题,给出合理的总结,特别是points和points_2之间的对比。

                记住,只返回json数据,无需其他中文。
                """;

            // ========== 构建聊天历史 ==========
            List<Microsoft.Extensions.AI.ChatMessage> chatMessages = new List<Microsoft.Extensions.AI.ChatMessage>
            {
                new Microsoft.Extensions.AI.ChatMessage(ChatRole.System, """
                    你是专业的充电设备过温分析助手,具备无线充电相关知识。
                    回答要专业、准确、易懂。
                    """),
                new Microsoft.Extensions.AI.ChatMessage(ChatRole.User, userPrompt)
            };

            // ========== 流式输出 AI 响应 ==========
            Console.WriteLine("\n🤖 AI 分析报告:\n");
            string response = "";

            try
            {
                await foreach (ChatResponseUpdate item in client.GetStreamingResponseAsync(chatMessages))
                {
                    Console.Write(item.Text);
                    response += item.Text;
                }
                Console.WriteLine("\n");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"\n❌ AI 响应失败:{ex.Message}");
                return;
            }

            // ========== 保存对话历史 ==========
            chatMessages.Add(new Microsoft.Extensions.AI.ChatMessage(ChatRole.Assistant, response));

            // 可选:保存到文件
            await File.WriteAllTextAsync($"battery_analysis_{DateTime.Now:yyyyMMdd_HHmmss}.txt", response);
            Console.WriteLine("✅ 分析报告已保存");
        }

        /// <summary>
        /// 调用电池数据 API{"Deviceno":"C2412A0210","Rxsn":"2510100071","stime":"2026-03-18T15:01:48","etime":"2026-03-19T01:28:00","pageNumber":1,"pageSize":1000}  
        /// </summary>
        static List<CleanResult> GetBatteryDataAsync()
        {
            List<CleanResult> results = new List<CleanResult>();
            using (var context = new postgerPublicDbContext())
            {
                var rst = context.Chargerecords.Where(A => A.Rxsngroupid == "54954cd9-a088-4e22-a770-d20f5fdb31f3" && A.Begintime > Convert.ToDateTime("2026-05-05") && A.Begintime < Convert.ToDateTime("2026-05-15") && A.Chargetime > 360);
                foreach(var item in rst)
                {
                    results.Add(new CleanResult() { txsn = item.Txsn, createtime = item.Begintime.Value });
                }
            }
            

            foreach (var item in results)
            {
                var pointData = OtsHelper.QueryPointChargeingData(new SearchPointChargeingDto() { Txsn = item.txsn, stime = item.createtime, etime = item.createtime.AddHours(10), pageNumber = 1, pageSize = 10000, Msgtype=4 });
                if (!pointData.data.Any(A => A.Tt > 89))
                {
                    continue;
                }
                item.points = pointData.data.Select(A => new PointChargeingResult() { Createtime = A.Createtime, Ri = Convert.ToDouble(A.Ri)/ Convert.ToDouble(100), Rv = Convert.ToDouble(A.Rv) / Convert.ToDouble(100), Ti = Convert.ToDouble(A.Ti) / Convert.ToDouble(100), Tt = A.Tt, Tv = Convert.ToDouble(A.Tv) / Convert.ToDouble(100), Txsn = A.Txsn, PWM = A.PWM }).Where(A=>((A.Tt>25&&A.Tt<50))).ToList();

                item.points_2 = pointData.data.Select(A => new PointChargeingResult() { Createtime = A.Createtime, Ri = Convert.ToDouble(A.Ri) / Convert.ToDouble(100), Rv = Convert.ToDouble(A.Rv) / Convert.ToDouble(100), Ti = Convert.ToDouble(A.Ti) / Convert.ToDouble(100), Tt = A.Tt, Tv = Convert.ToDouble(A.Tv) / Convert.ToDouble(100), Txsn = A.Txsn, PWM = A.PWM }).Where(A => ((A.Tt > 80))).ToList();
            }
            return results;
        }
    }


    public class DeviceAlmTxsnCleaner
    {

        /// <summary>
        /// 数据清洗 - 10 小时窗口内取中间时间点
        /// </summary>
        /// <param name="data">原始数据列表</param>
        /// <returns>清洗后的 txsn 和时间点</returns>
        public List<CleanResult> CleanData(List<DeviceAlmTxsnOtsResult> data)
        {
            // 1. 按 txsn 分组
            var grouped = data
                .Where(x => !string.IsNullOrEmpty(x.txsn) && x.policetime > 0)
                .GroupBy(x => x.txsn)
                .ToList();

            var results = new List<CleanResult>();

            foreach (var group in grouped)
            {
                // 2. 将时间戳转换为 DateTime 并排序
                var sortedItems = group
                    .Select(x => new
                    {
                        Original = x,
                        Time = DateTimeOffset.FromUnixTimeMilliseconds(x.policetime).DateTime
                    })
                    .OrderBy(x => x.Time)
                    .ToList();

                // 3. 按 10 小时窗口分组
                var windows = new List<List<dynamic>>();
                var currentWindow = new List<dynamic>();

                foreach (var item in sortedItems)
                {
                    if (currentWindow.Count == 0)
                    {
                        currentWindow.Add(item);
                    }
                    else
                    {
                        var firstTime = currentWindow.First().Time;
                        var currentTime = item.Time;

                        // 如果与窗口第一个时间相差 <= 10 小时,加入当前窗口
                        if ((currentTime - firstTime).TotalHours <= 10)
                        {
                            currentWindow.Add(item);
                        }
                        else
                        {
                            // 否则保存当前窗口,开始新窗口
                            windows.Add(currentWindow);
                            currentWindow = new List<dynamic> { item };
                        }
                    }
                }

                // 添加最后一个窗口
                if (currentWindow.Count > 0)
                {
                    windows.Add(currentWindow);
                }

                // 4. 每个窗口取中间时间点
                foreach (var window in windows)
                {
                    if (window.Count == 0) continue;

                    DateTime midTime;
                    if (window.Count == 1)
                    {
                        // 只有一条记录,直接取该时间
                        midTime = window.First().Time;
                    }
                    else
                    {
                        // 多条记录,取中间时间点
                        var minTime = window.Min(x => x.Time);
                        var maxTime = window.Max(x => x.Time);
                        midTime = minTime.AddMinutes((maxTime - minTime).TotalMinutes / 2);
                    }

                    results.Add(new CleanResult
                    {
                        txsn = group.Key,
                        createtime = midTime
                    });
                }
            }

            return results.OrderBy(x => x.createtime).ToList();
        }
    }
    /// <summary>
    /// 清洗结果
    /// </summary>
    public class CleanResult
    {
        [Description("发射端编号")]
        public string txsn { get; set; }

        [Description("中间时间点")]
        public DateTime createtime { get; set; }

        public List<PointChargeingResult> points { get; set; }
        public List<PointChargeingResult> points_2 { get; set; }
    }
    public class PointChargeingResult
    {

        public long Createtime { get; set; }
        public DateTime CreatetimeCn { get { return TimeHelper.GetTime(Createtime, true); } }
        public string Txsn { get; set; }

        public double Tv { get; set; }
        public double Rv { get; set; }
        public double Ri { get; set; }
        public long Tt { get; set; }
        public double Ti { get; set; }
        public long PWM { get; set; }
    }


}

 

posted @ 2026-06-16 15:15  天才卧龙  阅读(5)  评论(0)    收藏  举报