重写自动分配的逻辑

This commit is contained in:
xingheng 2025-10-01 00:18:02 +08:00
parent 3ac7c7822a
commit b8b6fde9b0
4 changed files with 540 additions and 276 deletions

View File

@ -1,4 +1,5 @@
using DocumentFormat.OpenXml.Drawing.Spreadsheet;
using DocumentFormat.OpenXml.Math;
using DocumentFormat.OpenXml.Spreadsheet;
using Learun.Application.Organization;
using Learun.Application.TwoDevelopment.ZZDT_EC;
@ -11,6 +12,10 @@ using Learun.Util.SqlSugar;
using log4net.Config;
using Microsoft.Practices.ObjectBuilder2;
using Newtonsoft.Json;
using NPOI.POIFS.Crypt;
using NPOI.SS.Formula.Functions;
using OfficeOpenXml;
using OfficeOpenXml.Style;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Pipelines.Sockets.Unofficial.Arenas;
using SqlSugar;
@ -18,9 +23,13 @@ using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Web.Http;
using System.Windows;
using System.Windows.Input;
using static Learun.Application.TwoDevelopment.ZZDT_EC.IO_WorkFlowService;
using static Learun.Application.Web.Areas.ZZDT_EC.Controllers.ec_objecttypeController;
namespace Learun.Application.Web.AppApi
{
@ -49,14 +58,56 @@ namespace Learun.Application.Web.AppApi
/// 根据模板自动创建端子排
/// </summary>
/// <param name="projId"></param>
/// <param name="panelId"></param>
/// <param name="panelObj"></param>
/// <param name="seq"></param>
/// <param name="TSname"></param>
/// <param name="iOType"></param>
private ec_PanelStripEntity CreatePanelStripByProfile2(string projId, string TSname, string panelId, GlobalEnum.IOType iOType)
private ec_PanelStripEntity CreatePanelStripByProfile2(string projId, ec_PanelEntity panelObj, int seq, GlobalEnum.IOType iOType)
{
var setTb = ProjectSugar.TableName<ec_projectSettingsEntity>(projId);
var allSettings = SqlSugarHelper.Db.Queryable<ec_projectSettingsEntity>().AS(setTb).ToList();
allSettings = allSettings.FindAll(x => x.SettingName.StartsWith(GlobalObject.projSetting_IOCardProfile + iOType.ToString()));
//IO_CardProfile
return new ec_PanelStripEntity();
//IO_CardProfile_DO_TermNoPerCh
//IO_CardProfile_DO_CHNoPerStrip
//IO_CardProfile_DO_ChNamePrefix
//IO_CardProfile_DO_ChNameStartIndex
var sName = GlobalObject.projSetting_IOCardProfile + iOType.ToString() + "_TermNoPerCh";
var TermNoPerCh = allSettings.FirstOrDefault(x => x.SettingName == sName)?.SettingValue;
sName = GlobalObject.projSetting_IOCardProfile + iOType.ToString() + "_CHNoPerStrip";
var CHNoPerStrip = allSettings.FirstOrDefault(x => x.SettingName == sName)?.SettingValue;
sName = GlobalObject.projSetting_IOCardProfile + iOType.ToString() + "_ChNamePrefix";
var ChNamePrefix = allSettings.FirstOrDefault(x => x.SettingName == sName)?.SettingValue;
if (int.TryParse(TermNoPerCh, out int iTermNoPerCh))
{
//err
}
if (int.TryParse(CHNoPerStrip, out int iCHNoPerStrip))
{
//err
}
var newTs = new ec_PanelStripEntity()
{
IO_TYPE = iOType.ToString(),
PanelID = panelObj.PanelID,
Panel_Strip_Seq = seq,
StripName = "TS_" + iOType.ToString() + "_" + seq,
TagNumber = "CreatePanelStripByProfile2"
};
newTs.Create();
for (int i = 0; i < iCHNoPerStrip; i++)
{
var newCh = new ec_PanelChannelEntity()
{
ChannelName = ChNamePrefix + (i + 1).ToString(),
Channel_Seq = i + 1,
StripID = newTs.StripID,
};
newCh.Create();
newTs.Channels.Add(newCh);
}
return newTs;
}
/// <summary>
/// 找到某一个预分配箱子 附近的某一个箱子。且io类型能匹配上
@ -394,6 +445,36 @@ namespace Learun.Application.Web.AppApi
}
ICache redisObj = CacheFactory.CaChe();
/// <summary>
/// 导出分配结果
/// </summary>
/// <param name="projectId"></param>
/// <param name="flg">是否为真实结果。step2时为falsestep3后为true</param>
[HttpGet]
public void AutoAssignCable2Channel_ResExport(string projectId, bool flg)
{
var BLL = new ec_Wire_GroupBLL();
var bytes = new byte[] { };
try
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (var package = new ExcelPackage())//新建
{
var sheet = package.Workbook.Worksheets.Add("test");
sheet.Cells[1, 1].Value = "test";
bytes = package.GetAsByteArray();
}
}
catch (Exception ex)
{
log4net.LogManager.GetLogger("ERROR").Error("ec_PanelChannelBLLIOModuleExportExcel 监控系统表导出" + ex.Message + ex.StackTrace);
bytes = System.Text.Encoding.UTF8.GetBytes(ex.Message + ex.StackTrace);
}
var stream = new MemoryStream(bytes);
FileDownHelper.DownLoad(stream, $"自动分配结果{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx");//自动化平台导出excel
}
/// <summary>
/// 自动分配通道(点表信号自动分配)。
///
@ -522,9 +603,7 @@ namespace Learun.Application.Web.AppApi
ICache redisObj = CacheFactory.CaChe();
var cablesNeedAssigned = redisObj.Read<List<ec_CableEntity>>("IOModule_AutoAssign2Ch_" + projId, CacheId.IOModule_AutoAssign2Ch);
var setTb = ProjectSugar.TableName<ec_projectSettingsEntity>(projId);
var allSettings = SqlSugarHelper.Db.Queryable<ec_projectSettingsEntity>().AS(setTb).ToList();
//IO_CardProfile
var signalTb = ProjectSugar.TableName<ec_Wire_GroupEntity>(projId);
var allSignals = SqlSugarHelper.Db.Queryable<ec_Wire_GroupEntity>().AS(signalTb).ToList();
@ -535,9 +614,9 @@ namespace Learun.Application.Web.AppApi
.InnerJoin<ec_PanelStripTermEntity>((a, b) => a.TermID == b.TermID).AS<ec_PanelStripTermEntity>(termTb)
.Select((a, b) => b.ChannelID).Distinct().ToList();
var allUsedCH = allSignals.Where(x => !string.IsNullOrEmpty(x.ChannelID)).Select(x => x.ChannelID).Distinct().ToList();
allUsedCH.AddRange(allConnedTerms);
//包括2部分1是已经被信号占用的通道2是已经被接线端子占用的通道
var allUsedCHBySignalOrSet = allSignals.Where(x => !string.IsNullOrEmpty(x.ChannelID)).Select(x => x.ChannelID).Distinct().ToList();
allUsedCHBySignalOrSet.AddRange(allConnedTerms);
//??这里有问题。通道是否被占用需要看signal和set双重在io分配界面
//1. 信号有, 电缆set有占了
//2.信号no电缆set有占了
@ -545,32 +624,32 @@ namespace Learun.Application.Web.AppApi
//4.信号no电缆setno没占
//1.2 流程图 分组原则为同一信号类型、同一系统的为一组
var cablesGrouped = cablesNeedAssigned.OrderBy(x => x.PanelID).ThenBy(x => x.PreAssignIOType).ThenBy(x => x.System).ToList();
var cablesGrouped = cablesNeedAssigned.OrderBy(x => x.AssignedPanel?.PanelID).ThenBy(x => x.PreAssignIOType).ThenBy(x => x.System).ToList();
cablesGrouped = cablesGrouped.Where(x => x.Sets != null && x.Sets.Count() > 0 && x.AssignedPanel != null).ToList();//过滤掉没有set的或者没有找到箱子的
cablesGrouped = cablesGrouped.Where(x => x.Sets.Where(xx => !string.IsNullOrEmpty(xx.PreAssignGroup_Desc)).Count() > 0).ToList();//过滤掉set没有分配信号的
var allPanelIds = cablesGrouped.Select(x => x.PanelID).Distinct();
var allPanelIds = cablesGrouped.Select(x => x.AssignedPanel).Select(x => x.PanelID).Distinct().ToList();
var stripBll = new ec_PanelStripBLL();
var allStrips = stripBll.GetList("{ProjectId:\"" + projId + "\"}", OnlySelf: false)
.Where(x => allPanelIds.Contains(x.PanelID))
.GroupBy(x => x.PanelID)
.ToDictionary(x => x.Key, x => x.ToList());
//感觉逻辑上用panel来循环会更合理
.ToDictionary(x => x.Key, x => x.ToList());//带出strip下的channel
foreach (var curPanelId in allPanelIds)
{
int newTSSeq = 10001;
var curStrips = allStrips[curPanelId];
var cablesOnThisPanel = cablesGrouped.Where(x => x.PanelID == curPanelId).ToList();
if (cablesOnThisPanel == null || cablesOnThisPanel.Count == 0)
{
continue;//next panel
}
//var lastCableSystemAndIOType = cablesOnThisPanel.First().System + cablesOnThisPanel.First().PreAssignIOType;//用于判断是否是同一个组
var existedStrips = allStrips[curPanelId];//已经存在的所有模块
var cablesOnThisPanel = cablesGrouped.Where(x => x.AssignedPanel.PanelID == curPanelId).ToList();
if (cablesOnThisPanel == null || cablesOnThisPanel.Count == 0) continue;//next panel
var lastUsedStrip = (ec_PanelStripEntity)null;
foreach (var cable in cablesOnThisPanel)
{
if (cable.CableID == "8c8ebc52-1ae3-4074-af53-e914dd178e78")
{
Console.Write("");
}
;
//1.2.2 分组原则为同一信号类型、同一系统的为一组系统属性从电缆的from端上的设备中取设备的系统同组的优先放到同一个箱子的同一个模块里面
//如果一组放完后发现模块还有多余的通道可以放则下一个系统继续从这个模块开始分配。
//???总感觉这句话,总结后:可以无脑用上一个模块,直到模块满了再用下一个模块。
@ -590,159 +669,22 @@ namespace Learun.Application.Web.AppApi
if (cable.PreAssignIOType.ToLower() == GlobalEnum.signalType.Digital.ToString().ToLower())
{
if (setsIn.Count > 0 && setsOut.Count > 0)
{
//from cjj 25 09 23 wechat:一根电缆可能会出现既有输入也有输出,如果碰到就放到两个端子排里面,公共端不会出现一个输出,一个输入的
continue;
}
//todo
if (setsIn.Count > 0 && setsOut.Count > 0) continue;
//from cjj 25 09 23 wechat:一根电缆可能会出现既有输入也有输出,如果碰到就放到两个端子排里面,公共端不会出现一个输出,一个输入的
}
#endregion
#region inputNew
if (setsIn.Count > 0)
{
var resIn = AutoAssignCore(GlobalEnum.inOrOut., setsIn, setsSpared);
var resIn = AutoAssignCore(GlobalEnum.inOrOut., setsIn);
}
#endregion
#region input
//ec_PanelStripEntity curStrip = null;
//var matchedStrips = curStrips.Where(x => x.IO_TYPE == ioTypeOnCable.ToString()).ToList();
//if (matchedStrips != null && lastUsedStrip != null && lastUsedStrip.IO_TYPE == ioTypeOnCable.ToString())
//{
// //1.2.2 优先使用上一个模块,直到模块满了再用下一个模块。
// matchedStrips.Insert(0, lastUsedStrip);
//}
//if (setsIn.Count == 0 )
//{
// //相当于没有
//}
//else if (setsIn.Count + setsSpared.Count() > 10)
//{
// //另外如果电缆对数大于10对即像12*2*0.75这种无论它预分配了多少根电缆对永远预留4个以下的空白通道。
// //意思就是一个模块里面够4个或4个以上空白通道就留4个空白通道
// //如果不够4个就留4个以下即可。
//}
//else
//{
// string PanelName = cable.AssignedPanel.TagNumber;//当前电缆预分配的箱子名称
// switch (cable.PreAssignIOType.ToLower())
// {
// case "digital":
// //数字量
// ioTypeOnCable = GlobalEnum.IOType.DI;
// break;
// case "4~20ma":
// //模拟量4-20mA
// ioTypeOnCable = GlobalEnum.IOType.AI;
// break;
// case "10v":
// ioTypeOnCable = GlobalEnum.IOType.TenVolt;
// break;
// case "pt100":
// ioTypeOnCable = GlobalEnum.IOType.PT100;
// break;
// case "pulse":
// ioTypeOnCable = GlobalEnum.IOType.PULSE;
// break;
// default:
// //通讯类 485 422啥的
// continue;
// break;
// }
// //1.2 流程图 箱子里是否已经存在端子排io匹配
// bool alreadyInsertNewTs = false;
// if (matchedStrips == null || matchedStrips.Count == 0)
// {
// //没有端子排
// //1.2 流程图 根据信号数里自动新建端子排,端子排通道数里根据箱子模板中的默认值获得
// var newTS = CreatePanelStripByProfile2(projId, "TS_" + ioTypeOnCable.ToString() + "_" + newTSSeq++, curPanelId, ioTypeOnCable);
// alreadyInsertNewTs = true;
// matchedStrips.Add(newTS);
// }
// #region 1.2 流程图 空的通道数够不够
// if (cable.CableClass == SWS.Share.Enum.cableClass.homerun.ToString())
// {
// //1.2.32如果模块是通讯信号像RS485这种就是通讯信号则不用管预留空白通道这个事情看到有空的通道就放。
// //当前这个端子排就是可用的
// curStrip = matchedStrips.First();
// }
// else
// {
// //1.2.3 (1) 非通讯如果空的通道大于整个模块的5%则可以利用利用到小于5%了则停止利用如果本身空通道数占的比例就小于5%,则不利用。
// curStrip = FindNextAvailableTS(matchedStrips, allUsedCH);
// if (curStrip == null)
// {
// var newTS = CreatePanelStripByProfile2(projId, "TS_" + ioTypeOnCable.ToString() + "_" + newTSSeq++, curPanelId, ioTypeOnCable);
// alreadyInsertNewTs = true;
// matchedStrips.Add(newTS);
// curStrip = newTS;
// }
// }
// var usedChs = curStrip.Channels.Where(x => allUsedCH.Contains(x.ChannelID)).Select(x => x.ChannelID).ToList();
// var notUsedChs = curStrip.Channels.Where(x => !usedChs.Contains(x.ChannelID)).ToList();
// #endregion
// #region 1.2 流程图 按规则先放几个信号进去
// if (notUsedChs.Count < setsIn.Count + setsSpared.Count())
// {
// //不够放,又要新建?
// if (alreadyInsertNewTs)
// {
// //压根就有问题说明电缆的set太多了就算是新建的模块一个channel都没有使用的情况下都塞不下。
// continue;
// }
// else
// {
// //相当于前面5%的判断过了但是呢放不下所有需要的set
// var newTS = CreatePanelStripByProfile2(projId, "TS_" + ioTypeOnCable.ToString() + "_" + newTSSeq++, curPanelId, ioTypeOnCable);
// alreadyInsertNewTs = true;
// matchedStrips.Add(newTS);
// curStrip = newTS;
// usedChs = curStrip.Channels.Where(x => allUsedCH.Contains(x.ChannelID)).Select(x => x.ChannelID).ToList();
// notUsedChs = curStrip.Channels.Where(x => !usedChs.Contains(x.ChannelID)).ToList();
// if (notUsedChs.Count < setsIn.Count + setsSpared.Count())
// {
// //压根就有问题说明电缆的set太多了就算是新建的模块一个channel都没有使用的情况下都塞不下。
// continue;
// }
// }
// }
// lastUsedStrip = curStrip;
// //能放下
// for (int i = 0; i < setsIn.Count; i++)
// {
// var set = setsIn[i];
// var ch = notUsedChs[i];
// set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排){curStrip.StripName}/通道:{ch.ChannelName}";
// //更新全局已使用通道
// allUsedCH.Add(ch.ChannelID);
// }
// var chOffIdx = setsIn.Count;
// for (int i = 0; i < setsSpared.Count(); i++)
// {
// var set = setsSpared[i];
// var ch = notUsedChs[chOffIdx + i];
// set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排){curStrip.StripName}/通道:{ch.ChannelName}/冗余";
// //更新全局已使用通道
// allUsedCH.Add(ch.ChannelID);
// }
// #endregion
//}
#endregion
#endregion
#region output
if (setsOut.Count > 0)
{
var resOut = AutoAssignCore(GlobalEnum.inOrOut., setsOut, setsSpared);
var resOut = AutoAssignCore(GlobalEnum.inOrOut., setsOut);
}
@ -750,7 +692,7 @@ namespace Learun.Application.Web.AppApi
#endregion
//in 和 out都会执行这个
bool AutoAssignCore(GlobalEnum.inOrOut inOrOut, List<ec_CableSetEntity> sets, List<ec_CableSetEntity> sets_Spared)
bool AutoAssignCore(GlobalEnum.inOrOut inOrOut, List<ec_CableSetEntity> sets)
{
GlobalEnum.IOType ioTypeOnC = default;
switch (cable.PreAssignIOType.ToLower())
@ -792,111 +734,444 @@ namespace Learun.Application.Web.AppApi
return false;
}
ec_PanelStripEntity curStrip = null;
var matchedStrips = curStrips.Where(x => x.IO_TYPE == ioTypeOnC.ToString()).ToList();
var matchedStrips = existedStrips.Where(x => x.IO_TYPE == ioTypeOnC.ToString()).ToList();//这个电缆下符合的模块
if (matchedStrips != null && lastUsedStrip != null && lastUsedStrip.IO_TYPE == ioTypeOnC.ToString())
{
//1.2.2 优先使用上一个模块,直到模块满了再用下一个模块。
matchedStrips.Insert(0, lastUsedStrip);
matchedStrips.Remove(lastUsedStrip);
matchedStrips.Insert(0, lastUsedStrip);//
}
if (matchedStrips == null || matchedStrips.Count == 0)
{
#region NewTS
//1.2 流程图 根据信号数里自动新建端子排,端子排通道数里根据箱子模板中的默认值获得
var newTS = CreatePanelStripByProfile2(projId, cable.AssignedPanel, newTSSeq++, ioTypeOnC);
matchedStrips.Add(newTS);
#endregion
}
string PanelName = cable.AssignedPanel.TagNumber;//当前电缆预分配的箱子名称
if (sets.Count == 0)
if (sets.Count == 0) return false;
//1.2 流程图 箱子里是否已经存在端子排io匹配
List<ec_PanelChannelEntity> usedChs = new List<ec_PanelChannelEntity>();
List<ec_PanelChannelEntity> notUsedChs = new List<ec_PanelChannelEntity>();
if (cable.CableClass == SWS.Share.Enum.cableClass.homerun.ToString())
{
//相当于没有
//1.2.32如果模块是通讯信号像RS485这种就是通讯信号则不用管预留空白通道这个事情看到有空的通道就放。
//当前这个端子排就是可用的
//
}
else
#region 1.2
//if (cable.Sets.Count > 10)
//{
// //另外如果电缆对数大于10对即像12 * 2 * 0.75这种无论它预分配了多少根电缆对永远预留4个以下的空白通道。意思就是一个模块里面够4个或4个以上空白通道就留4个空白通道如果不够4个就留4个以下即可。
// #region FindNextAvailableTS可能多个
// //还分2种情况判断模块是否符合
// //case1. 占满可用通道后还剩至少一个预分配的set和spared的set。 //这种时候要spared不用考虑
// //case2. 占满可用通道后或者没占满通道预分配set已经没了只剩下spared的set。 //这种时候要确保spared也能放下
// bool NeedNextTS = false;
// do
// {
// #region FindNextAvailableTS
// var availableTS = new List<ec_PanelStripEntity>();
// foreach (var TS in matchedStrips)
// {
// var allCh = TS.Channels;
// //被占用的(wt里有或者信号里或有或者被锁定的都算占用
// usedChs = allCh.Where(x => allUsedCHBySignalOrSet.Contains(x.ChannelID) || x.lock_flg == 1).ToList();
// notUsedChs = allCh.Where(x => !usedChs.Select(c => c.ChannelID).Contains(x.ChannelID)).ToList();
// if (notUsedChs.Count < sets.Count)
// {
// //case1能顺利接完set还有多
// //这种时候不需要考虑spared
// }
// else
// {
// //case2,占满可用通道后或者没占满通道预分配set已经没了只剩下spared的set
// //想办法留出4个
// //这种时候要确保spared也能放下
// if (notUsedChs.Count < sets.Count + setsSpared.Count)
// {
// //放不下,换下一个模块
// continue;
// }
// var sparedSetFor10 = notUsedChs.Count - sets.Count - setsSpared.Count;//16-9-5=2
// if (sparedSetFor10 > 4)
// {
// //固定流出后面4个相当于不要了
// var last4 = allCh.Skip(allCh.Count - 4).ToList();
// usedChs = allCh.Take(allCh.Count - 4).Where(x => allUsedCHBySignalOrSet.Contains(x.ChannelID) || x.lock_flg == 1).ToList();
// usedChs.AddRange(last4);
// notUsedChs = allCh.Take(allCh.Count - 4).Where(x => !usedChs.Select(c => c.ChannelID).Contains(x.ChannelID)).ToList();
// }
// else
// {
// //不够4个就留多少是多少
// var lastN = allCh.Skip(allCh.Count - sparedSetFor10).ToList();
// usedChs = allCh.Take(allCh.Count - sparedSetFor10).Where(x => allUsedCHBySignalOrSet.Contains(x.ChannelID) || x.lock_flg == 1).ToList();
// usedChs.AddRange(lastN);
// notUsedChs = allCh.Take(allCh.Count - sparedSetFor10).Where(x => !usedChs.Select(c => c.ChannelID).Contains(x.ChannelID)).ToList();
// }
// }
// //1.2 流程图 空的通道数够不够
// double sparedRate = notUsedChs.Count * 1.0 / allCh.Count * 1.0;
// if (sparedRate < 0.05)
// {
// //没有空闲通道 next
// //或者空闲通道不够放下这个电缆的所有set
// }
// else
// {
// TS.Channels = allCh.OrderBy(X => X.Channel_Seq).ToList();//不能像之前一样过滤掉,否则下一个电缆进来时,总数就不对了。
// TS.ChannelsSpared = notUsedChs.Count;
// TS.ChannelsUsed = usedChs.Count;
// availableTS.Add(TS);//所有5%以上空闲通道的模块
// break;
// }
// }
// #endregion
// #region NewTS
// if (availableTS.Count() == 0)
// {
// //1.2 流程图 根据信号数里自动新建端子排,端子排通道数里根据箱子模板中的默认值获得
// var newTS = CreatePanelStripByProfile2(projId, cable.AssignedPanel, newTSSeq++, ioTypeOnC);
// //matchedStrips.Add(newTS);
// var allCh = newTS.Channels;
// notUsedChs = newTS.Channels.ToList();
// newTS.ChannelsSpared = notUsedChs.Count;
// newTS.ChannelsUsed = 0;
// usedChs = new List<ec_PanelChannelEntity>();//新建的没有被占用的
// availableTS.Add(newTS);
// if (notUsedChs.Count < sets.Count)
// {
// //case1能顺利接完set还有多
// //这种时候不需要考虑spared
// }
// else
// {
// //case2,占满可用通道后或者没占满通道预分配set已经没了只剩下spared的set
// //想办法留出4个
// //这种时候要确保spared也能放下
// if (notUsedChs.Count < sets.Count + setsSpared.Count)
// {
// //放不下,换下一个模块
// continue;
// }
// var sparedSetFor10 = notUsedChs.Count - sets.Count - setsSpared.Count;//16-9-5=2
// if (sparedSetFor10 > 4)
// {
// //固定流出后面4个相当于不要了
// var last4 = allCh.Skip(allCh.Count - 4).ToList();
// usedChs = allCh.Take(allCh.Count - 4).Where(x => allUsedCHBySignalOrSet.Contains(x.ChannelID) || x.lock_flg == 1).ToList();
// usedChs.AddRange(last4);
// notUsedChs = allCh.Take(allCh.Count - 4).Where(x => !usedChs.Select(c => c.ChannelID).Contains(x.ChannelID)).ToList();
// }
// else
// {
// //不够4个就留多少是多少
// var lastN = allCh.Skip(allCh.Count - sparedSetFor10).ToList();
// usedChs = allCh.Take(allCh.Count - sparedSetFor10).Where(x => allUsedCHBySignalOrSet.Contains(x.ChannelID) || x.lock_flg == 1).ToList();
// usedChs.AddRange(lastN);
// notUsedChs = allCh.Take(allCh.Count - sparedSetFor10).Where(x => !usedChs.Select(c => c.ChannelID).Contains(x.ChannelID)).ToList();
// }
// }
// }
// #endregion
// lastUsedStrip = availableTS.Last();
// if (notUsedChs.Count < sets.Count)
// {
// //case1分配完后还有多的set和可能的spared
// //先这部分填完
// //到5%为止的数量
// var countOnThisTS = Math.Round(0.5 + notUsedChs.Count * 0.95);
// for (int i = 0; i < countOnThisTS; i++)
// {
// var set = sets[i];
// var ch = notUsedChs[i];
// if (lastUsedStrip.TagNumber == "CreatePanelStripByProfile2")
// {
// set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(自动创建){lastUsedStrip.StripName}/通道:{ch.ChannelName}";
// }
// else
// {
// set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(原本存在){lastUsedStrip.StripName}/通道:{ch.ChannelName}";
// }
// ch.FakeGroupDesc = set.PreAssignGroup_Desc;
// ch.FakeSignalType = set.IOType.ToString();
// ch.lock_flg = 1;
// set.IsConned = true;
// //更新全局已使用通道
// allUsedCHBySignalOrSet.Add(ch.ChannelID);
// }
// //set会剩
// NeedNextTS = true;
// }
// else
// {
// //case2分配完后没有多的set了可能还有spared
// //上面已经判断过了确保spared也是够的
// var countMin = Math.Min(sets.Count, notUsedChs.Count);
// for (int i = 0; i < countMin; i++)
// {
// var set = sets[i];
// var ch = notUsedChs[i];
// if (lastUsedStrip.TagNumber == "CreatePanelStripByProfile2")
// {
// set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(自动创建){lastUsedStrip.StripName}/通道:{ch.ChannelName}";
// }
// else
// {
// set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(原本存在){lastUsedStrip.StripName}/通道:{ch.ChannelName}";
// }
// ch.FakeGroupDesc = set.PreAssignGroup_Desc;
// ch.FakeSignalType = set.IOType.ToString();
// ch.lock_flg = 1;
// set.IsConned = true;
// //更新全局已使用通道
// allUsedCHBySignalOrSet.Add(ch.ChannelID);
// }
// #region spared
// for (int i = 0; i < setsSpared.Count(); i++)
// {
// var set = setsSpared[i];
// var ch = notUsedChs[countMin + i];
// if (lastUsedStrip.TagNumber == "CreatePanelStripByProfile2")
// {
// set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(自动创建){lastUsedStrip.StripName}/通道:{ch.ChannelName}/冗余";
// }
// else
// {
// set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(原本存在){lastUsedStrip.StripName}/通道:{ch.ChannelName}/冗余";
// }
// ch.FakeGroupDesc = "预留";
// ch.lock_flg = 1;
// //更新全局已使用通道
// allUsedCHBySignalOrSet.Add(ch.ChannelID);
// }
// NeedNextTS = false;
// #endregion
// }
// sets = sets.Where(x => !x.IsConned).ToList();//移除已经分配过的set
// } while (NeedNextTS);
// #endregion
//}
//else
//{
//5%的空白通道
#region FindNextAvailableTS
//还分2种情况判断模块是否符合
//case1. 占满可用通道后还剩至少一个预分配的set和spared的set。 //这种时候要spared不用考虑
//case2. 占满可用通道后或者没占满通道预分配set已经没了只剩下spared的set。 //这种时候要确保spared也能放下
//case3, 刚好只有spared可能多
bool NeedNextTS = false;
#region FindNextAvailableTS
int idx = 0;
while (idx < matchedStrips.Count)
{
var totalSets = sets.Count + setsSpared.Count();
//1.2 流程图 箱子里是否已经存在端子排io匹配
bool alreadyInsertNewTs = false;
if (matchedStrips == null || matchedStrips.Count == 0)
NeedNextTS = false;
var TS = matchedStrips[idx];
var allCh = TS.Channels;
var allChCount = allCh.Count;
if (allChCount == 0)
{
//没有端子排
//1.2 流程图 根据信号数里自动新建端子排,端子排通道数里根据箱子模板中的默认值获得
var newTS = CreatePanelStripByProfile2(projId, "TS_" + ioTypeOnC.ToString() + "_" + newTSSeq++, curPanelId, ioTypeOnC);
alreadyInsertNewTs = true;
matchedStrips.Add(newTS);
NeedNextTS = true;
//同时又是最后一个了
if (idx == matchedStrips.Count - 1)
{
var newTS = CreatePanelStripByProfile2(projId, cable.AssignedPanel, newTSSeq++, ioTypeOnC);
if (newTS.Channels.Count() < setsSpared.Count + 1)//1代表至少要放一个set
{
//有问题了 panel的io模板估计会导致无限循环了
break;
}
matchedStrips.Add(newTS);
}
idx++; continue;//本模块没有一个ch
}
#region 1.2
if (cable.CableClass == SWS.Share.Enum.cableClass.homerun.ToString())
//被占用的(wt里有或者信号里或有或者被锁定的都算占用
usedChs = allCh.Where(x => allUsedCHBySignalOrSet.Contains(x.ChannelID) || x.lock_flg == 1).ToList();
notUsedChs = allCh.Where(x => !usedChs.Select(c => c.ChannelID).Contains(x.ChannelID)).ToList();
//1.2 流程图 空的通道数够不够
double sparedRate = notUsedChs.Count * 1.0 / allChCount * 1.0;
if (sparedRate < 0.05)
{
//1.2.32如果模块是通讯信号像RS485这种就是通讯信号则不用管预留空白通道这个事情看到有空的通道就放。
//当前这个端子排就是可用的
curStrip = matchedStrips.First();
NeedNextTS = true;
//同时又是最后一个了
if (idx == matchedStrips.Count - 1)
{
var newTS = CreatePanelStripByProfile2(projId, cable.AssignedPanel, newTSSeq++, ioTypeOnC);
if (newTS.Channels.Count() < setsSpared.Count + 1)//1代表至少要放一个set
{
//有问题了 panel的io模板估计会导致无限循环了
break;
}
matchedStrips.Add(newTS);
}
idx++; continue;//本模块可用的模块小于5%
}
//没有空闲通道 next
//或者空闲通道不够放下这个电缆的所有set
//算一下5%是多少个通道
var trueAvailableChs =notUsedChs.Count- (allChCount- FindMaxNumberChannel(allChCount));//真正能用的没接过的然后去掉5%
var ChNeedConn = 0;
var SparedNeedConn = 0;
if (trueAvailableChs < sets.Count)
{
//case1能顺利接完set还有多,还要再do循环一次,多余的set和spared都要放到下一个模块
NeedNextTS = true;
ChNeedConn = trueAvailableChs; SparedNeedConn = 0;
}
else if (trueAvailableChs == sets.Count)
{
//case3,刚好只有spared可能多
if (setsSpared.Count > 0)
{
//自己的感觉非客户word需求里的原话spared不能自己单独占用一个模块
NeedNextTS = true;
ChNeedConn = trueAvailableChs - 1;//留一个给spared
SparedNeedConn = 0;
}
else
{
ChNeedConn = trueAvailableChs; SparedNeedConn = 0;
}
}
else
{
//1.2.3 (1) 非通讯如果空的通道大于整个模块的5%则可以利用利用到小于5%了则停止利用如果本身空通道数占的比例就小于5%,则不利用。
curStrip = FindNextAvailableTS(cable.Sets.Count,totalSets, matchedStrips, allUsedCH);
if (curStrip == null)
//maxCh > sets.Count
//case2,占满可用通道后或者没占满通道预分配set已经没了只剩下spared的set
if (setsSpared.Count == 0)
{
var newTS = CreatePanelStripByProfile2(projId, "TS_" + ioTypeOnC.ToString() + "_" + newTSSeq++, curPanelId, ioTypeOnC);
alreadyInsertNewTs = true;
matchedStrips.Add(newTS);
curStrip = newTS;
//ok
ChNeedConn = sets.Count; SparedNeedConn = 0;
}
}
var usedChs = curStrip.Channels.Where(x => allUsedCH.Contains(x.ChannelID)).Select(x => x.ChannelID).ToList();
var notUsedChs = curStrip.Channels.Where(x => !usedChs.Contains(x.ChannelID)).ToList();
#endregion
#region 1.2
if (notUsedChs.Count < totalSets)
{
//不够放,又要新建?
if (alreadyInsertNewTs)
//这种时候要确保spared也能放下
else if (trueAvailableChs < sets.Count + setsSpared.Count)
{
//压根就有问题说明电缆的set太多了就算是新建的模块一个channel都没有使用的情况下都塞不下。
return false;
//自己的感觉非客户word需求里的原话spared不能自己单独占用一个模块
ChNeedConn = trueAvailableChs - 1;//留一个给spared
NeedNextTS = true;
SparedNeedConn = 0;
}
else
{
//相当于前面5%的判断过了但是呢放不下所有需要的set
var newTS = CreatePanelStripByProfile2(projId, "TS_" + ioTypeOnC.ToString() + "_" + newTSSeq++, curPanelId, ioTypeOnC);
alreadyInsertNewTs = true;
matchedStrips.Add(newTS);
curStrip = newTS;
usedChs = curStrip.Channels.Where(x => allUsedCH.Contains(x.ChannelID)).Select(x => x.ChannelID).ToList();
notUsedChs = curStrip.Channels.Where(x => !usedChs.Contains(x.ChannelID)).ToList();
if (notUsedChs.Count < sets.Count + setsSpared.Count())
{
//压根就有问题说明电缆的set太多了就算是新建的模块一个channel都没有使用的情况下都塞不下。
return false;
}
{
//ok,set和spared都能放下
ChNeedConn = sets.Count;
SparedNeedConn = setsSpared.Count;
}
}
lastUsedStrip = curStrip;
for (int i = 0; i < sets.Count; i++)
//到这里,说明本模块至少都是可用的
TS.Channels = allCh.OrderBy(X => X.Channel_Seq).ToList();//不能像之前一样过滤掉,否则下一个电缆进来时,总数就不对了。
lastUsedStrip = TS;
#region conn
for (int i = 0; i < ChNeedConn; i++)
{
var set = sets[i];
var ch = notUsedChs[i];
set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排){curStrip.StripName}/通道:{ch.ChannelName}";
if (lastUsedStrip.TagNumber == "CreatePanelStripByProfile2")
{
set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(自动创建){lastUsedStrip.StripName}/通道:{ch.ChannelName}";
}
else
{
set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(原本存在){lastUsedStrip.StripName}/通道:{ch.ChannelName}";
}
ch.FakeGroupDesc = set.PreAssignGroup_Desc;
ch.FakeSignalType = set.IOType.ToString();
ch.lock_flg = 1;
set.IsConned = true;
//更新全局已使用通道
allUsedCH.Add(ch.ChannelID);
allUsedCHBySignalOrSet.Add(ch.ChannelID);
}
var chOffIdx = sets.Count;
for (int i = 0; i < setsSpared.Count(); i++)
#endregion
#region spared
for (int i = 0; i < SparedNeedConn; i++)
{
var set = setsSpared[i];
var ch = notUsedChs[chOffIdx + i];
set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排){curStrip.StripName}/通道:{ch.ChannelName}/冗余";
var ch = notUsedChs[ChNeedConn + i];
if (lastUsedStrip.TagNumber == "CreatePanelStripByProfile2")
{
set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(自动创建){lastUsedStrip.StripName}/通道:{ch.ChannelName}/冗余";
}
else
{
set.ConnectionInfo = $"采集箱:{PanelName}/模块(端子排)(原本存在){lastUsedStrip.StripName}/通道:{ch.ChannelName}/冗余";
}
ch.lock_flg = 1;
//更新全局已使用通道
allUsedCH.Add(ch.ChannelID);
allUsedCHBySignalOrSet.Add(ch.ChannelID);
}
#endregion
//shi否要继续
TS.ChannelsSpared = notUsedChs.Where(x=>x.lock_flg !=1).Count();
TS.ChannelsUsed = allChCount - TS.ChannelsSpared;// usedChs.Count + (notUsedChs.Where(x => x.lock_flg == 1).Count());
if (NeedNextTS)
{
sets = sets.Where(x => !x.IsConned ?? false).ToList();//移除已经分配过的set
//同时又是最后一个了
if (idx == matchedStrips.Count - 1)
{
var newTS = CreatePanelStripByProfile2(projId, cable.AssignedPanel, newTSSeq++, ioTypeOnC);
if (newTS.Channels.Count() < setsSpared.Count + 1)//1代表至少要放一个set
{
//有问题了 panel的io模板估计会导致无限循环了
break;
}
matchedStrips.Add(newTS);
}
idx++; continue;// 需要下一个模块接着放本电缆
// 手动递增索引,避免无限循环
}
else
{
break;
}
}
#endregion
#endregion
//}
#endregion
return true;
}
@ -909,50 +1184,32 @@ namespace Learun.Application.Web.AppApi
}
/// <summary>
/// 找到下一个可用的端子排
/// 找到满足5%空闲通道的最大数
/// </summary>
/// <param name="setCount">电缆总set</param>
/// <param name="TSs">所有的端子排</param>
/// <param name="allUsedCH">使用过的channelId</param>
/// <param name="ChCount">模块的所有通道数</param>
/// <returns></returns>
private ec_PanelStripEntity FindNextAvailableTS(int setCount,int setPreAssigned, List<ec_PanelStripEntity> TSs, List<string> allUsedCH)
/// <exception cref="ArgumentException"></exception>
/// <exception cref="InvalidOperationException"></exception>
static int FindMaxNumberChannel(int ChCount)
{
foreach (var TS in TSs)
if (ChCount < 1)
{
var validChs = TS.Channels.Where(x => x.lock_flg == 0).ToList();//过滤掉锁定的通道
var usedChs = validChs.Where(x => allUsedCH.Contains(x.ChannelID)).Select(x => x.ChannelID).ToList();
var notUsedChs = validChs.Where(x => !usedChs.Contains(x.ChannelID)).ToList();
double sparedRate = notUsedChs.Count * 1.0 / validChs.Count * 1.0;
if (sparedRate < 0.05 || notUsedChs.Count < setPreAssigned)
{
//没有空闲通道 next
//或者空闲通道不够放下这个电缆的所有set
}
else
{
TS.Channels = validChs.OrderBy(X => X.Channel_Seq).ToList();
var seq = TSs.IndexOf(TS);
if (setCount > 10)
{
//另外如果电缆对数大于10对即像12 * 2 * 0.75这种无论它预分配了多少根电缆对永远预留4个以下的空白通道。意思就是一个模块里面够4个或4个以上空白通道就留4个空白通道如果不够4个就留4个以下即可。
if (notUsedChs.Count < 4)
{
//没有足够的空闲通道 next
continue;
}
}
return TS;//找到了
}
throw new ArgumentException("模块数量必须大于等于1", nameof(ChCount));
}
// 从A-1开始向下寻找
for (int i = 0; i < ChCount; i++)
{
// 检查是否大于5%
if ((double)i / ChCount < 0.05 && (double)(i + 1) / ChCount > 0.05)
{
return ChCount - i;
}
}
return null;//都没有
return ChCount;
}
/// <summary>
/// 根据step2的预分配结果进行实际的分配修改数据库。
/// </summary>

View File

@ -148,7 +148,7 @@ namespace Learun.Application.TwoDevelopment.ZZDT_EC
/// 是否被连接
/// </summary>
[SugarColumn(IsIgnore = true)]
public bool? IsConned { set; get; }
public bool? IsConned { set; get; } = false;
/// <summary>
/// 当前这个set的连接情况
@ -168,8 +168,7 @@ namespace Learun.Application.TwoDevelopment.ZZDT_EC
#endregion
public ec_CableSetEntity()
{
IsConned = null;
{
}
}
}

View File

@ -117,7 +117,7 @@ namespace Learun.Application.TwoDevelopment.ZZDT_EC
this.ChannelID = Guid.NewGuid().ToString();
this.CreateTime = Time.MySqlTime;
UserInfo userInfo = LoginUserInfo.Get();
this.CreateUserID = userInfo.userId;
this.CreateUserID = userInfo?.userId;
}

View File

@ -73,7 +73,7 @@ namespace Learun.Application.TwoDevelopment.ZZDT_EC
this.StripID = Guid.NewGuid().ToString();
this.CreateTime = Time.MySqlTime;
UserInfo userInfo = LoginUserInfo.Get();
this.CreateUserID = userInfo.userId;
this.CreateUserID = userInfo?.userId;
}
@ -96,15 +96,23 @@ namespace Learun.Application.TwoDevelopment.ZZDT_EC
/// </summary>
[SugarColumn(IsIgnore = true)]
public List<ec_PanelChannelEntity> Channels { set; get; } = new List<ec_PanelChannelEntity>();
/// <summary>
/// channel在<see cref="ec_WireTerminalEntity"/>里有,同时<see cref="ec_Wire_GroupEntity.ChannelID"/>也有
/// </summary>
[SugarColumn(IsIgnore = true)]
public int ChannelsUsed { set; get; } = 0;
/// <summary>
/// channel在<see cref="ec_WireTerminalEntity"/>里有,但是没有信号
/// </summary>
[SugarColumn(IsIgnore = true)]
public int ChannelsUsedNoSignal { set; get; } = 0;
/// <summary>
/// channel在<see cref="ec_WireTerminalEntity"/>里没有
/// </summary>
[SugarColumn(IsIgnore = true)]
public int ChannelsSpared { set; get; } = 0;
[SugarColumn(IsIgnore = true)]
public string TagNumber { set; get; }