@@ -0,0 +1,101 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace BPA.Model.Attributes | |||||
{ | |||||
public class VariableMonitorAttribute : Attribute | |||||
{ | |||||
/// <summary> | |||||
/// 变量描述 | |||||
/// </summary> | |||||
/// <param name="Notes">描述</param> | |||||
/// <param name="PLCAddress">PLC 地址</param> | |||||
/// <param name="ModbusTcpAddress">Modbus TCP 地址</param> | |||||
public VariableMonitorAttribute(string Notes, string PLCAddress = "", string ModbusTcpAddress = "") | |||||
{ | |||||
this.PLCAddress = PLCAddress; | |||||
this.ModbusTcpAddress = GetModbusTcpAdd(PLCAddress);// ModbusTcpAddress; | |||||
this.Notes = Notes; | |||||
} | |||||
private string GetModbusTcpAdd(string address) | |||||
{ | |||||
if (address == null) | |||||
return ""; | |||||
if (address.Length > 0) | |||||
{ | |||||
address = address.Trim(); | |||||
if (address.ToUpper().Contains("GM") && address.Length >= 3) | |||||
{ | |||||
var res = address.Remove(0, 2); | |||||
if (res != null && res.Length > 0) | |||||
return (int.Parse(res) + 4096).ToString(); | |||||
} | |||||
else if (address.ToUpper().Contains("M") && address.Length >= 4) | |||||
{ | |||||
var res = address.Substring(1).Split('.'); | |||||
if (res != null && res.Length == 2) | |||||
{ | |||||
if (int.TryParse(res[0], out int firstAddress) && int.TryParse(res[1], out int ExitAddress)) | |||||
{ | |||||
if (ExitAddress >= 0 && ExitAddress <= 7) | |||||
{ | |||||
return ((firstAddress * 8) + 320 + ExitAddress).ToString(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else if (address.ToUpper().Contains("GI") && address.Length >= 3) | |||||
{ | |||||
var res = address.Remove(0, 2); | |||||
if (res != null && res.Length > 0) | |||||
return res; | |||||
} | |||||
else if (address.ToUpper().Contains("LB") && address.Length >= 3) | |||||
{ | |||||
var res = address.Substring(2); | |||||
if (res != null && res.Length > 0) | |||||
{ | |||||
if (int.TryParse(res, out int firstAddress)) | |||||
return firstAddress.ToString(); | |||||
} | |||||
} | |||||
else if ((address.ToUpper().Contains("VW") || address.ToUpper().Contains("VD")) && address.Length >= 3) | |||||
{ | |||||
var res = address.Substring(2); | |||||
if (res != null && int.TryParse(res, out int tempAddress)) | |||||
{ | |||||
return ((tempAddress / 2) + 100).ToString(); | |||||
} | |||||
} | |||||
else if (address.ToUpper().Contains("LW") && address.Length >= 3) | |||||
{ | |||||
var res = address.Substring(2); | |||||
if (res != null && int.TryParse(res, out int LwAddress)) | |||||
{ | |||||
return LwAddress.ToString(); | |||||
} | |||||
} | |||||
} | |||||
return ""; | |||||
} | |||||
/// <summary> | |||||
/// PLC 地址 | |||||
/// </summary> | |||||
public string PLCAddress { get; set; } | |||||
/// <summary> | |||||
/// Modbus TCP 地址 | |||||
/// </summary> | |||||
public string ModbusTcpAddress { get; set; } | |||||
/// <summary> | |||||
/// 描述 | |||||
/// </summary> | |||||
public string Notes { get; set; } | |||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>net6.0-windows</TargetFramework> | <TargetFramework>net6.0-windows</TargetFramework> | ||||
@@ -6,23 +6,44 @@ using System.Threading.Tasks; | |||||
using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
using BPA.Model.Recipe; | using BPA.Model.Recipe; | ||||
using BPA.Model.Enums; | using BPA.Model.Enums; | ||||
using BPA.SingleDevice.Interface; | |||||
using BPA.Model.Attributes; | |||||
namespace BPA.Model | namespace BPA.Model | ||||
{ | { | ||||
public class GlobalData | |||||
public class GlobalData: IStatus | |||||
{ | { | ||||
public ConcurrentQueue<GoodsModel> GoodsModels { get; set; } = new ConcurrentQueue<GoodsModel>(); | |||||
/// <summary> | /// <summary> | ||||
/// 需要执行的配方队列。 | /// 需要执行的配方队列。 | ||||
/// </summary> | /// </summary> | ||||
public ConcurrentQueue<RecipeData> RecipeQueue { get; set; } = new(); | public ConcurrentQueue<RecipeData> RecipeQueue { get; set; } = new(); | ||||
#region 配料机 | #region 配料机 | ||||
/// <summary> | |||||
/// 是否配料完成。 | |||||
/// </summary> | |||||
[VariableMonitor("配料机完成状态")] | |||||
public bool[] IsCompleted { get; set; } = new bool[4]; | |||||
#endregion | #endregion | ||||
#region 传送带 | #region 传送带 | ||||
[VariableMonitor("传送带移动状态步")] | |||||
public MoveConveyerStep MoveConveyerStep { get; set; } | public MoveConveyerStep MoveConveyerStep { get; set; } | ||||
[VariableMonitor("传送带移动完成")] | |||||
public bool ConveyerMoveComplete { get; set; } | |||||
[VariableMonitor("工站容器到位检测")] | |||||
public bool[] HaveVessel { get; set; } = new bool[5]; | |||||
#endregion | |||||
#region 全局流程相关 | |||||
[VariableMonitor("待制作配方数量")] | |||||
public int WaitMakeRecipeCount { get=>RecipeQueue.Count; } | |||||
[VariableMonitor("开机目前完成配方数量")] | |||||
public int CompletedCount { get; set; } | |||||
#endregion | #endregion | ||||
} | } | ||||
} | } |
@@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace BPA.SingleDevice.Interface | |||||
{ | |||||
public interface IStatus | |||||
{ | |||||
} | |||||
} |
@@ -30,6 +30,14 @@ namespace BPA.Model.Recipe | |||||
/// 下发时间 | /// 下发时间 | ||||
/// </summary> | /// </summary> | ||||
public DateTime IssueTime { get; set; } | public DateTime IssueTime { get; set; } | ||||
/// <summary> | |||||
/// 开始制作时间 | |||||
/// </summary> | |||||
public DateTime StartTime { get; set; } | |||||
/// <summary> | |||||
/// 制作完成时间。 | |||||
/// </summary> | |||||
public DateTime CompleteTime { get; set; } | |||||
public Dictionary<int,BatchStep> BatchStatus { get; set; } | public Dictionary<int,BatchStep> BatchStatus { get; set; } | ||||
public RecipeData(string id,string name, Dictionary<int, ushort[]> materialData,int stationCount=5) | public RecipeData(string id,string name, Dictionary<int, ushort[]> materialData,int stationCount=5) | ||||
{ | { | ||||
@@ -0,0 +1,73 @@ | |||||
using BPA.Helper; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace BPA.Model | |||||
{ | |||||
public class VariableMonitor : NotifyBase | |||||
{ | |||||
public int Id { get { return _mId; } set { _mId = value; OnPropertyChanged(); } } | |||||
private int _mId; | |||||
public string VarName { get { return _mVarName; } set { _mVarName = value; OnPropertyChanged(); } } | |||||
private string _mVarName; | |||||
public string PLCAddress { get { return _mPLCAddress; } set { _mPLCAddress = value; OnPropertyChanged(); } } | |||||
private string _mPLCAddress; | |||||
public string Notes { get { return _mNotes; } set { _mNotes = value; OnPropertyChanged(); } } | |||||
private string _mNotes; | |||||
public string ModbusTcpAddress { get { return _mModbusTcpAddress; } set { _mModbusTcpAddress = value; OnPropertyChanged(); } } | |||||
private string _mModbusTcpAddress; | |||||
public string CurrentValue | |||||
{ | |||||
get { return _mCurrentValue; } | |||||
set | |||||
{ | |||||
_mCurrentValue = value; | |||||
OnPropertyChanged(); | |||||
if (_mCurrentValue == "False" || _mCurrentValue == "True") | |||||
{ | |||||
if (_mCurrentValue == "True") | |||||
StatusColor = new { r = 51, g = 232, b = 34, a = 1 }; | |||||
else | |||||
StatusColor = new { r = 255, g = 0, b = 0, a = 1 }; | |||||
} | |||||
else | |||||
StatusColor = new { r = 51, g = 232, b = 34, a = 1 }; | |||||
} | |||||
} | |||||
private string _mCurrentValue; | |||||
public bool SwitchValue | |||||
{ | |||||
get { return _sWitchValue; } | |||||
set | |||||
{ | |||||
_sWitchValue = value; | |||||
OnPropertyChanged(); | |||||
} | |||||
} | |||||
private bool _sWitchValue; | |||||
public int IntValue | |||||
{ | |||||
get { return _intValue; } | |||||
set | |||||
{ | |||||
_intValue = value; | |||||
OnPropertyChanged(); | |||||
} | |||||
} | |||||
private int _intValue; | |||||
public object StatusColor { get; set; } | |||||
} | |||||
} |
@@ -15,8 +15,8 @@ namespace BPA.SingleDevice.Business | |||||
public bool[] HaveVessel { get; set; } | public bool[] HaveVessel { get; set; } | ||||
public int IsReverse { get; set ; } | public int IsReverse { get; set ; } | ||||
public float InchSpeed { get; set; } | |||||
public float MoveSpeed { get; set; } | |||||
public int InchSpeed { get; set; } | |||||
public int MoveSpeed { get; set; } | |||||
public int AccTime { get; set; } | public int AccTime { get; set; } | ||||
public int MoveLength { get; set; } | public int MoveLength { get; set; } | ||||
public bool MoveComplete { get;set; } | public bool MoveComplete { get;set; } | ||||
@@ -69,7 +69,7 @@ namespace BPA.SingleDevice.Business | |||||
this.ID = id; | this.ID = id; | ||||
} | } | ||||
public void InchMove() | |||||
public void StartInchMove() | |||||
{ | { | ||||
if (IsConnected) | if (IsConnected) | ||||
{ | { | ||||
@@ -84,7 +84,21 @@ namespace BPA.SingleDevice.Business | |||||
} | } | ||||
} | } | ||||
} | } | ||||
public void StopInchMove() | |||||
{ | |||||
if (IsConnected) | |||||
{ | |||||
try | |||||
{ | |||||
modbus.Write<ushort>("VW0".ToModbusAdd(), 0); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
MessageLog.GetInstance.Show(ex.Message); | |||||
} | |||||
} | |||||
} | |||||
public bool MoveOnce() | public bool MoveOnce() | ||||
{ | { | ||||
if (IsConnected && AllowMove) | if (IsConnected && AllowMove) | ||||
@@ -105,13 +119,13 @@ namespace BPA.SingleDevice.Business | |||||
return false; | return false; | ||||
} | } | ||||
public bool SetInchParam(int isReverse, float inchSpeed) | |||||
public bool SetInchParam(int isReverse, int inchSpeed) | |||||
{ | { | ||||
if (IsConnected) | if (IsConnected) | ||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var result1= modbus.Write<float>("VD100".ToModbusAdd(), inchSpeed); | |||||
var result1= modbus.Write<int>("VD100".ToModbusAdd(), inchSpeed); | |||||
var result2= modbus.Write<int>("VW200".ToModbusAdd(), isReverse); | var result2= modbus.Write<int>("VW200".ToModbusAdd(), isReverse); | ||||
return result1.IsSuccess && result2.IsSuccess; | return result1.IsSuccess && result2.IsSuccess; | ||||
} | } | ||||
@@ -123,13 +137,13 @@ namespace BPA.SingleDevice.Business | |||||
return false; | return false; | ||||
} | } | ||||
public bool SetMoveParam(float moveSpeed, int accTime, int moveLength) | |||||
public bool SetMoveParam(int moveSpeed, int accTime, int moveLength) | |||||
{ | { | ||||
if (IsConnected) | if (IsConnected) | ||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var result1 = modbus.Write<float>("VD104".ToModbusAdd(), moveSpeed); | |||||
var result1 = modbus.Write<int>("VD104".ToModbusAdd(), moveSpeed); | |||||
var result2 = modbus.Write<int>("VW122".ToModbusAdd(), accTime); | var result2 = modbus.Write<int>("VW122".ToModbusAdd(), accTime); | ||||
var result3 = modbus.Write<int>("VD108".ToModbusAdd(), moveLength); | var result3 = modbus.Write<int>("VD108".ToModbusAdd(), moveLength); | ||||
return result1.IsSuccess && result2.IsSuccess && result3.IsSuccess; | return result1.IsSuccess && result2.IsSuccess && result3.IsSuccess; | ||||
@@ -28,16 +28,14 @@ namespace BPA.SingleDevice.Business | |||||
/// <summary> | /// <summary> | ||||
/// 键就是配料设备对应的ID。这里分别为1,2,4,5。 | /// 键就是配料设备对应的ID。这里分别为1,2,4,5。 | ||||
/// </summary> | /// </summary> | ||||
ConcurrentDictionary<int, IBatchcer> batchers = new(); | |||||
public ConcurrentDictionary<int, IBatchcer> Batchers { get; set; } = new(); | |||||
List<TaskServer> currentTask = new(); | |||||
//ConcurrentDictionary<int, TaskServer> test = new(); | |||||
List<RecipeData> currentRecipes = new(); | |||||
IConveyer conveyer = new Conveyer(); | |||||
public List<RecipeData> CurrentRecipes { get; set; } = new(); | |||||
private readonly ILogService logService; | private readonly ILogService logService; | ||||
private GlobalData global; | private GlobalData global; | ||||
public IConveyer Conveyer { get ; set ; } = new Conveyer(); | |||||
//RecipeData currentRecipe; | //RecipeData currentRecipe; | ||||
//CancellationTokenSource cts; | //CancellationTokenSource cts; | ||||
@@ -57,119 +55,99 @@ namespace BPA.SingleDevice.Business | |||||
Json<ConnectConfig>.Save(); | Json<ConnectConfig>.Save(); | ||||
} | } | ||||
#region 实例初始化配料机 | #region 实例初始化配料机 | ||||
batchers.TryAdd(1, new Batcher()); | |||||
batchers.TryAdd(2, new Batcher()); | |||||
batchers.TryAdd(4, new Batcher()); | |||||
batchers.TryAdd(5, new Batcher()); | |||||
//Batchers.TryAdd(1, new Batcher()); | |||||
//Batchers.TryAdd(2, new Batcher()); | |||||
//Batchers.TryAdd(4, new Batcher()); | |||||
//Batchers.TryAdd(5, new Batcher()); | |||||
//SetBatcherComm(1, "192.168.6.100"); | //SetBatcherComm(1, "192.168.6.100"); | ||||
//SetBatcherComm(2, "192.168.6.101"); | //SetBatcherComm(2, "192.168.6.101"); | ||||
//SetBatcherComm(4, "192.168.6.102"); | //SetBatcherComm(4, "192.168.6.102"); | ||||
//SetBatcherComm(5, "192.168.6.103"); | //SetBatcherComm(5, "192.168.6.103"); | ||||
SetBatcherComm(1, "127.0.0.1", 503); | |||||
SetBatcherComm(2, "127.0.0.1", 504); | |||||
SetBatcherComm(4, "127.0.0.1", 505); | |||||
SetBatcherComm(5, "127.0.0.1", 506); | |||||
foreach (var batcher in batchers.Values) | |||||
{ | |||||
await batcher.Initial(); | |||||
} | |||||
//SetBatcherComm(1, "127.0.0.1", 503); | |||||
//SetBatcherComm(2, "127.0.0.1", 504); | |||||
//SetBatcherComm(4, "127.0.0.1", 505); | |||||
//SetBatcherComm(5, "127.0.0.1", 506); | |||||
//foreach (var batcher in Batchers.Values) | |||||
//{ | |||||
// await batcher.Initial(); | |||||
//} | |||||
InitalBatcher(Json<ConnectConfig>.Data.BatcherConfigs); | |||||
#endregion | #endregion | ||||
//conveyer.SetCommParam(1, "192.168.6.104",508); | |||||
conveyer.SetCommParam(1, "127.0.0.1",510); | |||||
await conveyer.Initial(); | |||||
Conveyer.SetCommParam(1, "192.168.6.104",508); | |||||
//Conveyer.SetCommParam(1, "127.0.0.1",510); | |||||
await Conveyer.Initial(); | |||||
currentTask.Clear(); | |||||
CurrentRecipes.Clear(); | |||||
ActionRegister(); | ActionRegister(); | ||||
TaskManage.GetInstance.StartLong(() => | TaskManage.GetInstance.StartLong(() => | ||||
{ | { | ||||
InterActive(); | InterActive(); | ||||
//for (int i = 0; i < currentTask.Count; i++) | |||||
//{ | |||||
// if (currentTask[i].CurrentRecipe.IsMakeComplete.All(b=>b==true)) | |||||
// { | |||||
// currentTask.RemoveAt(i); | |||||
// break; | |||||
// } | |||||
//} | |||||
for (int i = 0; i < currentRecipes.Count; i++) | |||||
#region 移除配方 | |||||
for (int i = 0; i < CurrentRecipes.Count; i++) | |||||
{ | { | ||||
if (currentRecipes[i].IsMakeComplete.All(b => b == true)) | |||||
if (CurrentRecipes[i].IsMakeComplete.All(b => b == true)) | |||||
{ | { | ||||
var recipeName = currentRecipes[i].Name; | |||||
var time = DateTime.Now.Subtract(currentRecipes[i].IssueTime); | |||||
currentRecipes.RemoveAt(i); | |||||
logService.LogRecipeCompleteInfo($"【{recipeName}】,耗时【{time.Seconds}】秒。"); | |||||
logService.LogRunInfo($"配方【{recipeName}】-- 整体配料完成,已从配料队列移除。"); | |||||
var recipe = CurrentRecipes[i]; | |||||
recipe.CompleteTime = DateTime.Now; | |||||
var recipeName = recipe.Name; | |||||
var time = recipe.CompleteTime.Subtract(recipe.StartTime); | |||||
var issueTime = recipe.IssueTime.ToString("HH:mm:ss"); | |||||
CurrentRecipes.RemoveAt(i); | |||||
global.CompletedCount++; | |||||
logService.LogRecipeCompleteInfo($"【{recipeName}】,下发时间【{issueTime}】,开始制作时间【{recipe.StartTime.ToString("HH:mm:ss")}】,耗时【{(int)time.TotalSeconds}】秒。"); | |||||
logService.LogRunInfo($"配方【{recipeName}】,下发时间【{issueTime}】,开始制作时间【{recipe.StartTime.ToString("HH:mm:ss")}】-- 整体配料完成,已从配料队列移除。"); | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
#endregion | |||||
if (global.RecipeQueue.Count>0/* && currentRecipe is null*/ ) | |||||
#region 配方加入队列 | |||||
if (global.RecipeQueue.Count > 0 && CanIssueRecipe()) | |||||
{ | { | ||||
//多配方情况下,必须在工位1没有碗位置,才能进行下发,否则会与之前的配方冲突。 | |||||
//if (/*!conveyer.HaveVessel[0] &&*/ global.RecipeQueue.ElementAt(0) is not null && global.RecipeQueue.ElementAt(0) is RecipeData && currentTask.Count <5 ) | |||||
//{ | |||||
//global.RecipeQueue.TryDequeue(out currentRecipe); | |||||
//cts = new(); | |||||
//Task.Run(() => { Batching(currentRecipe); }, cts.Token); | |||||
//logService.LogRunInfo($"配方【{currentRecipe.Name}】-- 开始执行配料。"); | |||||
//global.RecipeQueue.TryDequeue(out RecipeData recipe); | |||||
//if (recipe !=null) | |||||
//{ | |||||
// currentTask.Add(new TaskServer() { RunTask = Task.Run(() => { Batching(recipe); }), | |||||
// ID = recipe.IssueTime.Ticks,CurrentRecipe=recipe }); | |||||
// logService.LogRunInfo($"配方【{recipe.Name}】-- 开始执行配料。"); | |||||
//} | |||||
//} | |||||
if (/*!conveyer.HaveVessel[0] &&*/ global.RecipeQueue.ElementAt(0) is not null && global.RecipeQueue.ElementAt(0) is RecipeData && currentRecipes.Count <5 ) | |||||
if (global.RecipeQueue.ElementAt(0) is not null && global.RecipeQueue.ElementAt(0) is RecipeData && CurrentRecipes.Count < 5) | |||||
{ | { | ||||
global.RecipeQueue.TryDequeue(out RecipeData recipe); | global.RecipeQueue.TryDequeue(out RecipeData recipe); | ||||
if (recipe != null) | if (recipe != null) | ||||
{ | { | ||||
currentRecipes.Add(recipe); | |||||
recipe.StartTime = DateTime.Now; | |||||
CurrentRecipes.Add(recipe); | |||||
logService.LogRunInfo($"配方【{recipe.Name}】-- 开始执行配料。"); | logService.LogRunInfo($"配方【{recipe.Name}】-- 开始执行配料。"); | ||||
} | } | ||||
} | } | ||||
} | |||||
} | |||||
#endregion | |||||
Batching(); | Batching(); | ||||
RefreshData(); | |||||
}, "MonitorRecipeIssue", true); | }, "MonitorRecipeIssue", true); | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 配料。 | |||||
/// 显示数据的赋值。 | |||||
/// </summary> | /// </summary> | ||||
/// <param name="recipe">需要配料的配方</param> | |||||
[Obsolete("流程有错误")] | |||||
private async void Batching(RecipeData recipe) | |||||
private void RefreshData() | |||||
{ | { | ||||
if (recipe is null) | |||||
{ | |||||
logService.LogRunInfo($"参数<recipe>值为null,配料流程结束。"); | |||||
return; | |||||
} | |||||
while (recipe.IsMakeComplete[0] == false) | |||||
global.IsCompleted[0] = Batchers[1].BatchComplete; | |||||
global.IsCompleted[1] = Batchers[2].BatchComplete; | |||||
global.IsCompleted[2] = Batchers[4].BatchComplete; | |||||
global.IsCompleted[3] = Batchers[5].BatchComplete; | |||||
for (int i = 0; i < Conveyer.HaveVessel.Length; i++) | |||||
{ | { | ||||
//await StationBatching(recipe, 1); | |||||
global.HaveVessel[i] = Conveyer.HaveVessel[i]; | |||||
} | } | ||||
for (int stationNum = 2; stationNum <= 5; stationNum++) | |||||
{ | |||||
await MoveOnceAndBatach(recipe, stationNum); | |||||
} | |||||
//ToDo:如果最后需要移动一次才能出餐,在这里添加一次移动传送带。 | |||||
logService.LogRunInfo($"配方【{recipe.Name}】配料完成。"); | |||||
logService.LogRecipeCompleteInfo($"【{recipe.Name}】"); | |||||
//currentRecipe = null; | |||||
global.ConveyerMoveComplete = Conveyer.MoveComplete; | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -181,13 +159,8 @@ namespace BPA.SingleDevice.Business | |||||
{ | { | ||||
#region 数据验证 | #region 数据验证 | ||||
//if (recipe is null) | |||||
//{ | |||||
// logService.LogRunInfo($"参数<recipe>值为null,配料流程结束。"); | |||||
// return; | |||||
//} | |||||
//如果配料机里没连接该工位的配料机,则直接完成。 | //如果配料机里没连接该工位的配料机,则直接完成。 | ||||
if (!batchers.ContainsKey(stationNum)) | |||||
if (!Batchers.ContainsKey(stationNum)) | |||||
{ | { | ||||
recipe.BatchStatus[stationNum] = BatchStep.BatchCompleted; | recipe.BatchStatus[stationNum] = BatchStep.BatchCompleted; | ||||
recipe.IsMakeComplete[stationNum - 1] = true; | recipe.IsMakeComplete[stationNum - 1] = true; | ||||
@@ -195,9 +168,9 @@ namespace BPA.SingleDevice.Business | |||||
return; | return; | ||||
} | } | ||||
#endregion | #endregion | ||||
//数组起始索引是0,工位起始ID是1。 | //数组起始索引是0,工位起始ID是1。 | ||||
if (conveyer.HaveVessel[stationNum-1]) | |||||
if (Conveyer.HaveVessel[stationNum-1]) | |||||
{ | { | ||||
ushort[] materialList=new ushort[14]; | ushort[] materialList=new ushort[14]; | ||||
//获取工位需要的配料数据。 | //获取工位需要的配料数据。 | ||||
@@ -213,9 +186,10 @@ namespace BPA.SingleDevice.Business | |||||
logService.LogRunInfo($"参数<stationNum>值为[{stationNum}],该配方无该工位配料需求。"); | logService.LogRunInfo($"参数<stationNum>值为[{stationNum}],该配方无该工位配料需求。"); | ||||
return; | return; | ||||
} | } | ||||
//配料机配料完成的上升沿检测。 | //配料机配料完成的上升沿检测。 | ||||
var completeTrig =RTrig.GetInstance($"Batchers[{stationNum}].BatchComplete").Start(batchers[stationNum].BatchComplete); | |||||
var completeTrig = RTrig.GetInstance($"Batchers[{stationNum}].BatchComplete").Start(Batchers[stationNum].BatchComplete); | |||||
//配料流程。 | //配料流程。 | ||||
switch (recipe.BatchStatus[stationNum]) | switch (recipe.BatchStatus[stationNum]) | ||||
{ | { | ||||
@@ -225,7 +199,7 @@ namespace BPA.SingleDevice.Business | |||||
case BatchStep.WriteBatchParam: | case BatchStep.WriteBatchParam: | ||||
if (materialList is not null && materialList.Length == 14) | if (materialList is not null && materialList.Length == 14) | ||||
{ | { | ||||
if (batchers[stationNum].WriteBatchData(materialList)) | |||||
if (Batchers[stationNum].WriteBatchData(materialList)) | |||||
{ | { | ||||
recipe.BatchStatus[stationNum] = BatchStep.StartBatch; | recipe.BatchStatus[stationNum] = BatchStep.StartBatch; | ||||
logService.LogRunInfo($"配方【{recipe.Name}】写入工位【{stationNum}】的下料参数【{String.Join(',',materialList)}】成功。"); | logService.LogRunInfo($"配方【{recipe.Name}】写入工位【{stationNum}】的下料参数【{String.Join(',',materialList)}】成功。"); | ||||
@@ -243,7 +217,7 @@ namespace BPA.SingleDevice.Business | |||||
} | } | ||||
break; | break; | ||||
case BatchStep.StartBatch: | case BatchStep.StartBatch: | ||||
if (batchers[stationNum].StartBatching()) | |||||
if (Batchers[stationNum].StartBatching()) | |||||
{ | { | ||||
recipe.BatchStatus[stationNum] = BatchStep.WaitBatchComplete; | recipe.BatchStatus[stationNum] = BatchStep.WaitBatchComplete; | ||||
logService.LogRunInfo($"配方【{recipe.Name}】工位【{stationNum}】开始配料,等待配料完成信号上升沿。"); | logService.LogRunInfo($"配方【{recipe.Name}】工位【{stationNum}】开始配料,等待配料完成信号上升沿。"); | ||||
@@ -272,49 +246,6 @@ namespace BPA.SingleDevice.Business | |||||
Task.Delay(3000).Wait(); | Task.Delay(3000).Wait(); | ||||
} | } | ||||
} | } | ||||
[Obsolete("流程有错误")] | |||||
private async Task MoveOnceAndBatach(RecipeData recipe,int stationNum) | |||||
{ | |||||
global.MoveConveyerStep = MoveConveyerStep.WaitMove; | |||||
while (global.MoveConveyerStep != MoveConveyerStep.MoveComplete) | |||||
{ | |||||
var moveCompleteTrig = RTrig.GetInstance("MoveCompleted").Start(conveyer.MoveComplete); | |||||
switch (global.MoveConveyerStep) | |||||
{ | |||||
case MoveConveyerStep.WaitMove: | |||||
conveyer.InitalMoveParam(); | |||||
await Task.Delay(500); | |||||
if (conveyer.MoveOnce()) | |||||
{ | |||||
logService.LogRunInfo($"配方【{recipe.Name}】控制传送带去下个工位,等待动作完成信号上升沿。"); | |||||
global.MoveConveyerStep = MoveConveyerStep.Moveing; | |||||
} | |||||
else | |||||
{ | |||||
logService.LogRunInfo($"配方【{recipe.Name}】控制传送带去下个工位失败,可能连接异常或不允许移动,3S后重试。"); | |||||
await Task.Delay(3000); | |||||
} | |||||
break; | |||||
case MoveConveyerStep.Moveing: | |||||
if (moveCompleteTrig) | |||||
{ | |||||
logService.LogRunInfo($"配方【{recipe.Name}】控制传送带移动结束。"); | |||||
conveyer.InitalMoveParam(); | |||||
while (recipe.IsMakeComplete[stationNum - 1] == false) | |||||
{ | |||||
//await StationBatching(recipe, stationNum); | |||||
} | |||||
global.MoveConveyerStep = MoveConveyerStep.MoveComplete; | |||||
} | |||||
break; | |||||
case MoveConveyerStep.MoveComplete: | |||||
//logService.LogRunInfo($"配方【{recipe.Name}】控制传送带移动结束。"); | |||||
//await StationBatching(recipe, stationNum); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// 移动传送带。 | /// 移动传送带。 | ||||
/// </summary> | /// </summary> | ||||
@@ -324,15 +255,16 @@ namespace BPA.SingleDevice.Business | |||||
//global.MoveConveyerStep = MoveConveyerStep.WaitMove; | //global.MoveConveyerStep = MoveConveyerStep.WaitMove; | ||||
//while (global.MoveConveyerStep != MoveConveyerStep.MoveComplete) | //while (global.MoveConveyerStep != MoveConveyerStep.MoveComplete) | ||||
//{ | //{ | ||||
var moveCompleteTrig = RTrig.GetInstance("MoveCompleted").Start(conveyer.MoveComplete); | |||||
var moveCompleteTrig = RTrig.GetInstance("MoveCompleted").Start(Conveyer.MoveComplete); | |||||
switch (global.MoveConveyerStep) | switch (global.MoveConveyerStep) | ||||
{ | { | ||||
case MoveConveyerStep.WaitMove: | case MoveConveyerStep.WaitMove: | ||||
conveyer.InitalMoveParam(); | |||||
Conveyer.InitalMoveParam(); | |||||
Task.Delay(500).Wait(); | Task.Delay(500).Wait(); | ||||
if (conveyer.MoveOnce()) | |||||
if (Conveyer.MoveOnce()) | |||||
{ | { | ||||
logService.LogRunInfo($"控制传送带去下个工位,等待动作完成信号上升沿。"); | logService.LogRunInfo($"控制传送带去下个工位,等待动作完成信号上升沿。"); | ||||
Task.Delay(500).Wait(); | |||||
global.MoveConveyerStep = MoveConveyerStep.Moveing; | global.MoveConveyerStep = MoveConveyerStep.Moveing; | ||||
} | } | ||||
else | else | ||||
@@ -350,7 +282,7 @@ namespace BPA.SingleDevice.Business | |||||
} | } | ||||
break; | break; | ||||
case MoveConveyerStep.MoveComplete: | case MoveConveyerStep.MoveComplete: | ||||
conveyer.InitalMoveParam(); | |||||
Conveyer.InitalMoveParam(); | |||||
UpdateRecipe(); | UpdateRecipe(); | ||||
global.MoveConveyerStep = MoveConveyerStep.WaitMove; | global.MoveConveyerStep = MoveConveyerStep.WaitMove; | ||||
//不会执行该步骤,会直接跳出循环。 | //不会执行该步骤,会直接跳出循环。 | ||||
@@ -366,9 +298,9 @@ namespace BPA.SingleDevice.Business | |||||
/// <param name="port">端口号,默认为502。</param> | /// <param name="port">端口号,默认为502。</param> | ||||
private void SetBatcherComm(int id,string ip,int port=502) | private void SetBatcherComm(int id,string ip,int port=502) | ||||
{ | { | ||||
if (batchers.ContainsKey(id)) | |||||
if (Batchers.ContainsKey(id)) | |||||
{ | { | ||||
batchers[id].SetCommParam(id, ip, port); | |||||
Batchers[id].SetCommParam(id, ip, port); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -380,27 +312,24 @@ namespace BPA.SingleDevice.Business | |||||
{ | { | ||||
ActionManage.GetInstance.Register<bool>(new Func<bool>(() => | ActionManage.GetInstance.Register<bool>(new Func<bool>(() => | ||||
{ | { | ||||
//目前如果工位1 没有碗或者当前没配方就可以下配方。 | |||||
//foreach (var item in currentTask) | |||||
//{ | |||||
// //起始工位没完成的不允许下配方。 | |||||
// if (!item.CurrentRecipe.IsMakeComplete[0]) | |||||
// { | |||||
// return false; | |||||
// } | |||||
//} | |||||
//return true; | |||||
//2023.10.14修改:如果在配方队列里,有配方的当前工位是1,则不允许下发新的配方。 | |||||
foreach (var item in currentRecipes) | |||||
return CanIssueRecipe(); | |||||
}), "CanIssueRecipe", true); | |||||
} | |||||
/// <summary> | |||||
/// 是否允许下发配方。 | |||||
/// </summary> | |||||
/// <returns></returns> | |||||
private bool CanIssueRecipe() | |||||
{ | |||||
foreach (var item in CurrentRecipes) | |||||
{ | |||||
//如果在配方队列里,有配方的当前工位是1,则不允许下发新的配方。 | |||||
if (item.CurrentStation == 1) | |||||
{ | { | ||||
if (item.CurrentStation==1) | |||||
{ | |||||
return false; | |||||
} | |||||
return false; | |||||
} | } | ||||
return true; | |||||
}), "CanIssueRecipe", true); | |||||
} | |||||
return global.MoveConveyerStep == MoveConveyerStep.WaitMove; | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 传送带和配料机之间的信号交互。 | /// 传送带和配料机之间的信号交互。 | ||||
@@ -408,14 +337,14 @@ namespace BPA.SingleDevice.Business | |||||
private void InterActive() | private void InterActive() | ||||
{ | { | ||||
#region 配料机 | #region 配料机 | ||||
foreach (var batcher in batchers.Values) | |||||
foreach (var batcher in Batchers.Values) | |||||
{ | { | ||||
batcher.AllowBatching = conveyer.HaveVessel[batcher.ID - 1]; | |||||
batcher.AllowBatching = Conveyer.HaveVessel[batcher.ID - 1]; | |||||
} | } | ||||
#endregion | #endregion | ||||
#region 传送带 | #region 传送带 | ||||
conveyer.AllowMove = /*conveyer.MoveComplete &&*/ GetBatcherAllowMove(); | |||||
Conveyer.AllowMove = GetBatcherAllowMove(); | |||||
#endregion | #endregion | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -425,9 +354,9 @@ namespace BPA.SingleDevice.Business | |||||
private bool GetBatcherAllowMove() | private bool GetBatcherAllowMove() | ||||
{ | { | ||||
foreach (var task in currentTask) | |||||
foreach (var recipe in CurrentRecipes) | |||||
{ | { | ||||
foreach (var item in task.CurrentRecipe.BatchStatus.Values) | |||||
foreach (var item in recipe.BatchStatus.Values) | |||||
{ | { | ||||
switch (item) | switch (item) | ||||
{ | { | ||||
@@ -447,7 +376,7 @@ namespace BPA.SingleDevice.Business | |||||
/// <returns></returns> | /// <returns></returns> | ||||
private bool IsAllowConveyerMove() | private bool IsAllowConveyerMove() | ||||
{ | { | ||||
foreach (var item in currentRecipes) | |||||
foreach (var item in CurrentRecipes) | |||||
{ | { | ||||
if (item.IsMakeComplete[item.CurrentStation-1]==false) | if (item.IsMakeComplete[item.CurrentStation-1]==false) | ||||
{ | { | ||||
@@ -483,17 +412,22 @@ namespace BPA.SingleDevice.Business | |||||
/// 初始化配料机。 | /// 初始化配料机。 | ||||
/// </summary> | /// </summary> | ||||
/// <param name="configs"></param> | /// <param name="configs"></param> | ||||
private void InitalBatcher(IList<BatcherConfig> configs) | |||||
private async void InitalBatcher(IList<BatcherConfig> configs) | |||||
{ | { | ||||
foreach (var item in configs) | foreach (var item in configs) | ||||
{ | { | ||||
if (item.IsConnect) | if (item.IsConnect) | ||||
{ | { | ||||
batchers.TryAdd(item.StationID, new Batcher()); | |||||
Batchers.TryAdd(item.StationID, new Batcher()); | |||||
SetBatcherComm(item.StationID, item.IP, item.Port); | SetBatcherComm(item.StationID, item.IP, item.Port); | ||||
batchers[item.StationID].Initial(); | |||||
} | } | ||||
} | } | ||||
foreach (var batcher in Batchers.Values) | |||||
{ | |||||
await batcher.Initial(); | |||||
} | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -501,16 +435,11 @@ namespace BPA.SingleDevice.Business | |||||
/// </summary> | /// </summary> | ||||
private void UpdateRecipe() | private void UpdateRecipe() | ||||
{ | { | ||||
foreach (var item in currentRecipes) | |||||
foreach (var item in CurrentRecipes) | |||||
{ | { | ||||
//if (item.CurrentStation<5 && conveyer.HaveVessel[item.CurrentStation]) | |||||
//{ | |||||
// item.CurrentStation++; | |||||
//} | |||||
if (item.CurrentStation<5) | if (item.CurrentStation<5) | ||||
{ | { | ||||
while (!conveyer.HaveVessel[item.CurrentStation]) | |||||
while (!Conveyer.HaveVessel[item.CurrentStation]) | |||||
{ | { | ||||
logService.LogRunInfo($"未检测到配方【{item.Name}】在工位【{item.CurrentStation + 1}】的容器到位信号,请检查。"); | logService.LogRunInfo($"未检测到配方【{item.Name}】在工位【{item.CurrentStation + 1}】的容器到位信号,请检查。"); | ||||
Task.Delay(3000).Wait(); | Task.Delay(3000).Wait(); | ||||
@@ -524,18 +453,22 @@ namespace BPA.SingleDevice.Business | |||||
/// </summary> | /// </summary> | ||||
private void Batching() | private void Batching() | ||||
{ | { | ||||
if (currentRecipes.Count>0 && IsAllowConveyerMove()) | |||||
if (CurrentRecipes.Count>0 && IsAllowConveyerMove()) | |||||
{ | { | ||||
MoveConveyer(); | MoveConveyer(); | ||||
} | } | ||||
foreach (var item in currentRecipes) | |||||
else if(global.MoveConveyerStep==MoveConveyerStep.WaitMove) | |||||
{ | { | ||||
if (!item.IsMakeComplete[item.CurrentStation-1]) | |||||
foreach (var item in CurrentRecipes) | |||||
{ | { | ||||
StationBatching(item, item.CurrentStation); | |||||
if (!item.IsMakeComplete[item.CurrentStation - 1]) | |||||
{ | |||||
StationBatching(item, item.CurrentStation); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -24,11 +24,11 @@ namespace BPA.SingleDevice.Interface | |||||
/// <summary> | /// <summary> | ||||
/// 寸动速度。 | /// 寸动速度。 | ||||
/// </summary> | /// </summary> | ||||
float InchSpeed { get; set; } | |||||
int InchSpeed { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// 传动带移动速度。 | /// 传动带移动速度。 | ||||
/// </summary> | /// </summary> | ||||
float MoveSpeed { get; set; } | |||||
int MoveSpeed { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// 加速时间,一般不改动。 | /// 加速时间,一般不改动。 | ||||
/// </summary> | /// </summary> | ||||
@@ -58,16 +58,20 @@ namespace BPA.SingleDevice.Interface | |||||
/// </summary> | /// </summary> | ||||
void SetCommParam(int id, string ip, int port = 502); | void SetCommParam(int id, string ip, int port = 502); | ||||
/// <summary> | /// <summary> | ||||
/// 寸动【调试状态】 | |||||
/// 开始寸动【调试状态】 | |||||
/// </summary> | /// </summary> | ||||
void InchMove(); | |||||
void StartInchMove(); | |||||
/// <summary> | |||||
/// 停止寸动。 | |||||
/// </summary> | |||||
void StopInchMove(); | |||||
/// <summary> | /// <summary> | ||||
/// 设置寸动参数 | /// 设置寸动参数 | ||||
/// </summary> | /// </summary> | ||||
/// <param name="isReverse">是否反转</param> | /// <param name="isReverse">是否反转</param> | ||||
/// <param name="inchSpeed">寸动速度</param> | /// <param name="inchSpeed">寸动速度</param> | ||||
/// <returns>设置是否成功</returns> | /// <returns>设置是否成功</returns> | ||||
bool SetInchParam(int isReverse,float inchSpeed); | |||||
bool SetInchParam(int isReverse,int inchSpeed); | |||||
/// <summary> | /// <summary> | ||||
/// 设置移动参数 | /// 设置移动参数 | ||||
/// </summary> | /// </summary> | ||||
@@ -75,7 +79,7 @@ namespace BPA.SingleDevice.Interface | |||||
/// <param name="accTime">移动加速时间</param> | /// <param name="accTime">移动加速时间</param> | ||||
/// <param name="moveLength">移动长度</param> | /// <param name="moveLength">移动长度</param> | ||||
/// <returns>设置是否成功</returns> | /// <returns>设置是否成功</returns> | ||||
bool SetMoveParam(float moveSpeed, int accTime, int moveLength); | |||||
bool SetMoveParam(int moveSpeed, int accTime, int moveLength); | |||||
/// <summary> | /// <summary> | ||||
/// 移动一次。 | /// 移动一次。 | ||||
/// </summary> | /// </summary> | ||||
@@ -1,4 +1,5 @@ | |||||
using System; | |||||
using BPA.Model.Recipe; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Text; | using System.Text; | ||||
@@ -12,5 +13,9 @@ namespace BPA.SingleDevice.Interface | |||||
/// 初始化即开始。 | /// 初始化即开始。 | ||||
/// </summary> | /// </summary> | ||||
void Inital(); | void Inital(); | ||||
List<RecipeData> CurrentRecipes { get; set; } | |||||
//ConcurrentBag<RecipeData> CurrentRecipes { get; set; } | |||||
IConveyer Conveyer { get; set; } | |||||
ConcurrentDictionary<int, IBatchcer> Batchers { get; set; } | |||||
} | } | ||||
} | } |
@@ -21,6 +21,7 @@ | |||||
Margin="10" | Margin="10" | ||||
HorizontalAlignment="Right" | HorizontalAlignment="Right" | ||||
bpa:ToggleButtonHelper.CheckedContent="调试模式" | bpa:ToggleButtonHelper.CheckedContent="调试模式" | ||||
x:Name="ModeButton" | |||||
Command="{Binding SwitchSystemModeCommand}" | Command="{Binding SwitchSystemModeCommand}" | ||||
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" | CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" | ||||
Content="配料模式" | Content="配料模式" | ||||
@@ -36,6 +37,7 @@ | |||||
<StackPanel | <StackPanel | ||||
HorizontalAlignment="Stretch" | HorizontalAlignment="Stretch" | ||||
VerticalAlignment="Top" | VerticalAlignment="Top" | ||||
IsEnabled="{Binding ElementName=ModeButton,Path=IsChecked}" | |||||
Orientation="Horizontal"> | Orientation="Horizontal"> | ||||
<Button | <Button | ||||
Width="100" | Width="100" | ||||
@@ -51,15 +51,14 @@ | |||||
Grid.Row="2" | Grid.Row="2" | ||||
Margin="0,5,0,0" | Margin="0,5,0,0" | ||||
bpa:GridHelper.ColumnDefinitions="*,*"> | bpa:GridHelper.ColumnDefinitions="*,*"> | ||||
<!--<bpa:NumericBox | |||||
<bpa:NumericBox | |||||
bpa:ControlHelper.FocusBorderBrush="{DynamicResource Secondary}" | bpa:ControlHelper.FocusBorderBrush="{DynamicResource Secondary}" | ||||
BorderThickness="0" | |||||
IsReadOnly="True" | |||||
BorderThickness="0" Width="90" | |||||
Style="{StaticResource FrontBackNumericBox}" | Style="{StaticResource FrontBackNumericBox}" | ||||
Value="{Binding Count}" />--> | |||||
Value="{Binding Count}" /> | |||||
<Button | <Button | ||||
Grid.Column="0" Grid.ColumnSpan="2" | |||||
Margin="10,0" | |||||
Grid.Column="1" | |||||
Margin="10,0,0,0" | |||||
Command="{Binding DataContext.DownRecipeCommand, RelativeSource={RelativeSource AncestorType=local:OrderMainView}}" | Command="{Binding DataContext.DownRecipeCommand, RelativeSource={RelativeSource AncestorType=local:OrderMainView}}" | ||||
CommandParameter="{Binding Id}" | CommandParameter="{Binding Id}" | ||||
Content="开始下单" | Content="开始下单" | ||||
@@ -1,12 +1,71 @@ | |||||
<UserControl x:Class="BPA.SingleDevice.View.ParamsSetView" | |||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||||
xmlns:local="clr-namespace:BPA.SingleDevice.View" | |||||
mc:Ignorable="d" | |||||
d:DesignHeight="450" d:DesignWidth="800"> | |||||
<UserControl | |||||
x:Class="BPA.SingleDevice.View.ParamsSetView" | |||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||||
xmlns:bpa="http://BPAUIControl.io/winfx/xaml/toolkit" | |||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||||
xmlns:local="clr-namespace:BPA.SingleDevice.View" | |||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||||
xmlns:vm="clr-namespace:BPA.SingleDevice.ViewModel" | |||||
d:DesignHeight="450" | |||||
d:DesignWidth="800" | |||||
mc:Ignorable="d"> | |||||
<Grid> | <Grid> | ||||
<Grid.RowDefinitions> | |||||
<RowDefinition Height="1*" /> | |||||
<RowDefinition Height="1*" /> | |||||
</Grid.RowDefinitions> | |||||
<!--#region 设备连接设置--> | |||||
<Border | |||||
Margin="5" | |||||
BorderBrush="Gray" | |||||
BorderThickness="3"> | |||||
<StackPanel Margin="5" Orientation="Horizontal" /> | |||||
</Border> | |||||
<!--#endregion--> | |||||
<!--#region 流水线参数设置--> | |||||
<Border | |||||
Grid.Row="1" | |||||
Margin="5" | |||||
BorderBrush="Gray" | |||||
BorderThickness="3"> | |||||
<StackPanel Margin="5" Orientation="Vertical"> | |||||
<Grid Margin="0,0,0,10"> | |||||
<TextBlock Text="流水线移动参数设置" FontSize="22" Foreground="White"/> | |||||
<Button | |||||
Width="100" | |||||
Height="40" | |||||
HorizontalAlignment="Right" | |||||
bpa:ButtonHelper.Shape="Round" | |||||
Command="{Binding WriteParamsCommand}" | |||||
Content="保存并写入设置" /> | |||||
</Grid> | |||||
<StackPanel Margin="5" Orientation="Horizontal"> | |||||
<TextBlock | |||||
FontSize="22" | |||||
Foreground="White" | |||||
Text="移动速度:" /> | |||||
<TextBox Width="120" Text="{Binding MoveSpeed}" /> | |||||
</StackPanel> | |||||
<StackPanel Margin="5" Orientation="Horizontal"> | |||||
<TextBlock | |||||
FontSize="22" | |||||
Foreground="White" | |||||
Text="加速时间:" /> | |||||
<TextBox Width="120" Text="{Binding AccTime}" /> | |||||
</StackPanel> | |||||
<StackPanel Margin="5" Orientation="Horizontal"> | |||||
<TextBlock | |||||
FontSize="22" | |||||
Foreground="White" | |||||
Text="移动长度:" /> | |||||
<TextBox Width="120" Text="{Binding MoveLength}" /> | |||||
</StackPanel> | |||||
</StackPanel> | |||||
</Border> | |||||
<!--#endregion--> | |||||
</Grid> | </Grid> | ||||
</UserControl> | </UserControl> |
@@ -6,7 +6,166 @@ | |||||
xmlns:local="clr-namespace:BPA.SingleDevice.View" | xmlns:local="clr-namespace:BPA.SingleDevice.View" | ||||
mc:Ignorable="d" | mc:Ignorable="d" | ||||
d:DesignHeight="450" d:DesignWidth="800"> | d:DesignHeight="450" d:DesignWidth="800"> | ||||
<Grid> | |||||
<Grid Margin="10"> | |||||
<Grid.RowDefinitions> | |||||
<RowDefinition Height="30" /> | |||||
<RowDefinition /> | |||||
</Grid.RowDefinitions> | |||||
<!--#region 表格标题栏设置--> | |||||
<Grid Background="#dd2AB2E7"> | |||||
<Grid.ColumnDefinitions> | |||||
<ColumnDefinition Width="0.3*" /> | |||||
<ColumnDefinition /> | |||||
<ColumnDefinition /> | |||||
<ColumnDefinition /> | |||||
<ColumnDefinition Width="0.7*" /> | |||||
<ColumnDefinition Width="0.7*" /> | |||||
</Grid.ColumnDefinitions> | |||||
<TextBlock | |||||
Grid.Column="0" | |||||
HorizontalAlignment="Center" | |||||
VerticalAlignment="Center" | |||||
FontSize="16" | |||||
Text="ID" /> | |||||
<Grid Grid.Column="1"> | |||||
<TextBlock | |||||
HorizontalAlignment="Center" | |||||
VerticalAlignment="Center" | |||||
FontSize="16" | |||||
Text="变量名" /> | |||||
<Border | |||||
BorderThickness="1,0,1,0" BorderBrush="Gray" | |||||
Cursor="SizeWE" /> | |||||
</Grid> | |||||
<TextBlock | |||||
Grid.Column="2" | |||||
HorizontalAlignment="Center" | |||||
VerticalAlignment="Center" | |||||
FontSize="16" | |||||
Text="PLC 地址" /> | |||||
<Grid Grid.Column="3"> | |||||
<TextBlock | |||||
HorizontalAlignment="Center" | |||||
VerticalAlignment="Center" | |||||
FontSize="16" | |||||
Text="注释" /> | |||||
<Border | |||||
BorderThickness="1,0,0,0" BorderBrush="Gray" | |||||
Cursor="SizeWE" /> | |||||
</Grid> | |||||
<Grid Grid.Column="4"> | |||||
<TextBlock | |||||
HorizontalAlignment="Center" | |||||
VerticalAlignment="Center" | |||||
FontSize="16" | |||||
Text="Modbus TCP 地址" /> | |||||
<Border | |||||
BorderThickness="1,0,1,0" BorderBrush="Gray" | |||||
Cursor="SizeWE" /> | |||||
</Grid> | |||||
<TextBlock | |||||
Grid.Column="5" | |||||
HorizontalAlignment="Center" | |||||
VerticalAlignment="Center" | |||||
FontSize="16" | |||||
Text="当前值" /> | |||||
</Grid> | |||||
<!--#endregion--> | |||||
<!--#region 表格数据显示--> | |||||
<ScrollViewer | |||||
Grid.Row="1" | |||||
HorizontalScrollBarVisibility="Hidden" | |||||
VerticalScrollBarVisibility="Auto"> | |||||
<ItemsControl ItemsSource="{Binding VariableMonitors}"> | |||||
<ItemsControl.ItemTemplate> | |||||
<DataTemplate> | |||||
<Grid x:Name="gr" Height="30"> | |||||
<Grid.ColumnDefinitions> | |||||
<ColumnDefinition Width="0.3*" /> | |||||
<ColumnDefinition /> | |||||
<ColumnDefinition /> | |||||
<ColumnDefinition /> | |||||
<ColumnDefinition Width="0.7*" /> | |||||
<ColumnDefinition Width="0.7*" /> | |||||
</Grid.ColumnDefinitions> | |||||
<TextBlock | |||||
Grid.Column="0" | |||||
HorizontalAlignment="Center" | |||||
VerticalAlignment="Center" | |||||
Background="Transparent" | |||||
FontSize="14" | |||||
Text="{Binding Id}" /> | |||||
<Grid Grid.Column="1"> | |||||
<TextBlock | |||||
Margin="5,0,0,0" | |||||
VerticalAlignment="Center" | |||||
Background="Transparent" | |||||
FontSize="14" | |||||
Text="{Binding VarName}" /> | |||||
<Border BorderBrush="Gray" BorderThickness="1,0,1,0" /> | |||||
</Grid> | |||||
<TextBlock | |||||
Grid.Column="2" | |||||
Margin="5,0,0,0" | |||||
VerticalAlignment="Center" | |||||
Background="Transparent" | |||||
FontSize="14" | |||||
Text="{Binding PLCAddress}" /> | |||||
<Grid Grid.Column="3"> | |||||
<TextBlock | |||||
Margin="5,0,0,0" | |||||
VerticalAlignment="Center" | |||||
Background="Transparent" | |||||
FontSize="14" | |||||
Text="{Binding Notes}" /> | |||||
<Border BorderBrush="Gray" BorderThickness="1,0,0,0" /> | |||||
</Grid> | |||||
<Grid Grid.Column="4"> | |||||
<TextBlock | |||||
Margin="5,0,0,0" | |||||
VerticalAlignment="Center" | |||||
Background="Transparent" | |||||
FontSize="14" | |||||
Foreground="White" | |||||
Text="{Binding ModbusTcpAddress}" /> | |||||
<Border BorderBrush="Gray" BorderThickness="1,0,1,0" /> | |||||
</Grid> | |||||
<TextBlock | |||||
Grid.Column="5" | |||||
Margin="5,0,0,0" | |||||
VerticalAlignment="Center" | |||||
Background="Transparent" | |||||
FontSize="14" Foreground="White" | |||||
Text="{Binding CurrentValue}" /> | |||||
<Border | |||||
Grid.ColumnSpan="8" | |||||
BorderBrush="Gray" | |||||
BorderThickness="1" /> | |||||
</Grid> | |||||
<DataTemplate.Triggers> | |||||
<Trigger Property="IsMouseOver" Value="true"> | |||||
<Setter TargetName="gr" Property="Background" Value="#112AB2E7" /> | |||||
</Trigger> | |||||
</DataTemplate.Triggers> | |||||
</DataTemplate> | |||||
</ItemsControl.ItemTemplate> | |||||
</ItemsControl> | |||||
</ScrollViewer> | |||||
<!--#endregion--> | |||||
</Grid> | </Grid> | ||||
</UserControl> | </UserControl> |
@@ -1,4 +1,6 @@ | |||||
using System; | |||||
using BPA.SingleDevice.Interface; | |||||
using BPA.SingleDevice.Services; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Text; | using System.Text; | ||||
@@ -8,9 +10,38 @@ namespace BPA.SingleDevice.ViewModel | |||||
{ | { | ||||
public class DebugViewModel:NotifyBase | public class DebugViewModel:NotifyBase | ||||
{ | { | ||||
public DebugViewModel() | |||||
private readonly IProcessControl process; | |||||
private readonly ILogService logService; | |||||
public DebugViewModel(IProcessControl process,ILogService logService) | |||||
{ | { | ||||
StartMoveInchCommand = new(() => | |||||
{ | |||||
if (process.CurrentRecipes.Count!=0) | |||||
{ | |||||
Message.ShowGlobal("当前有配方正在执行,不可对流水线进行寸动控制。"); | |||||
return; | |||||
} | |||||
var isSuccess= process.Conveyer?.SetInchParam(IsReverse ? 1 : 0, InchSpeed); | |||||
if (isSuccess is not null && isSuccess==true) | |||||
{ | |||||
process.Conveyer?.StartInchMove(); | |||||
Message.ShowGlobal("设置流水线寸动开始成功。"); | |||||
logService.LogUserInfo("设置流水线寸动开始。"); | |||||
} | |||||
else | |||||
{ | |||||
Message.ShowGlobal("设置流水线寸动开始失败,写入寸动参数失败了。"); | |||||
} | |||||
}); | |||||
StopMoveInchCommand = new(() => | |||||
{ | |||||
process.Conveyer.StopInchMove(); | |||||
Message.ShowGlobal("设置流水线寸动停止成功。"); | |||||
logService.LogUserInfo("设置流水线寸动停止。"); | |||||
}); | |||||
this.process = process; | |||||
this.logService = logService; | |||||
} | } | ||||
public BPARelayCommand<bool> SwitchSystemModeCommand { get; set; } | public BPARelayCommand<bool> SwitchSystemModeCommand { get; set; } | ||||
@@ -21,11 +52,11 @@ namespace BPA.SingleDevice.ViewModel | |||||
/// <summary> | /// <summary> | ||||
/// 开始寸动。 | /// 开始寸动。 | ||||
/// </summary> | /// </summary> | ||||
public BPARelayCommand EndMoveInchCommand { get; set; } | |||||
public BPARelayCommand StopMoveInchCommand { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// 寸动是否反转。 | /// 寸动是否反转。 | ||||
/// </summary> | /// </summary> | ||||
public bool IsReverse { get; set; } | public bool IsReverse { get; set; } | ||||
public float InchSpeed { get; set; } | |||||
public int InchSpeed { get; set; } = 1000; | |||||
} | } | ||||
} | } |
@@ -12,6 +12,7 @@ using BPA.Model.Recipe; | |||||
using BPA.SingleDevice.Services; | using BPA.SingleDevice.Services; | ||||
using System.Net.WebSockets; | using System.Net.WebSockets; | ||||
using System.Windows; | using System.Windows; | ||||
using Newtonsoft.Json; | |||||
namespace BPA.SingleDevice.ViewModel | namespace BPA.SingleDevice.ViewModel | ||||
{ | { | ||||
@@ -37,21 +38,26 @@ namespace BPA.SingleDevice.ViewModel | |||||
DownRecipeCommand = new BPARelayCommand<object>(o => | DownRecipeCommand = new BPARelayCommand<object>(o => | ||||
{ | { | ||||
if (o != null && !string.IsNullOrEmpty(o.ToString())) | |||||
var result = ActionManage.GetInstance.Send<bool>("CanIssueRecipe"); | |||||
if (result != null && result.IsSuccess && result.Content == true) | |||||
{ | { | ||||
if (MessageBoxR.ConfirmGlobal("是否下发配方?","提示")==MessageBoxResult.Yes) | |||||
if (o != null && !string.IsNullOrEmpty(o.ToString())) | |||||
{ | { | ||||
var result= ActionManage.GetInstance.Send<bool>("CanIssueRecipe"); | |||||
if (result != null && result.IsSuccess && result.Content==true) | |||||
if (MessageBoxR.ConfirmGlobal("是否下发配方?", "提示") == MessageBoxResult.Yes) | |||||
{ | { | ||||
GenerateRecipe(o.ToString()!); | GenerateRecipe(o.ToString()!); | ||||
} | } | ||||
else | |||||
{ | |||||
Message.ErrorGlobal("下发配方失败,当前有配方在执行工位【1】的配料流程,请检查后重试。"); | |||||
} | |||||
} | } | ||||
else | |||||
{ | |||||
Message.ErrorGlobal("下发配方失败,输入的配方ID为Null或空,请联系人员检查后。"); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
Message.ErrorGlobal("下发配方失败,当前有配方在执行工位【1】的配料流程,请检查后重试。"); | |||||
} | } | ||||
}); | }); | ||||
} | } | ||||
private List<RawMaterTB> RawMaters { get; set; } = new List<RawMaterTB>(); | private List<RawMaterTB> RawMaters { get; set; } = new List<RawMaterTB>(); | ||||
@@ -117,9 +123,13 @@ namespace BPA.SingleDevice.ViewModel | |||||
materialData.Add(device.Key, channelWeight); | materialData.Add(device.Key, channelWeight); | ||||
} | } | ||||
RecipeData recipeData = new(recipeID, recipeName, materialData); | RecipeData recipeData = new(recipeID, recipeName, materialData); | ||||
global.RecipeQueue.Enqueue(recipeData); | |||||
Message.SuccessGlobal($"下发配方【{recipeName}】成功。"); | |||||
logService.LogUserInfo($"下发配方【{recipeName}】成功。"); | |||||
var str = recipeData.ToJson(); | |||||
for (int i = 0; i < res.Count; i++) | |||||
{ | |||||
global.RecipeQueue.Enqueue(str.FromJSON<RecipeData>()); | |||||
} | |||||
Message.SuccessGlobal($"下发配方【{recipeName}】,份数【{res.Count}】成功。"); | |||||
logService.LogUserInfo($"下发配方【{recipeName}】,份数【{res.Count}】成功。"); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -127,55 +137,5 @@ namespace BPA.SingleDevice.ViewModel | |||||
} | } | ||||
} | } | ||||
} | } | ||||
/// <summary> | |||||
/// 之前的配方数据解析。 | |||||
/// </summary> | |||||
/// <param name="o"></param> | |||||
private void OldDataPrase(object o) | |||||
{ | |||||
var res = Goods.FirstOrDefault(p => p.Id == o.ToString()); | |||||
if (res != null) | |||||
{ | |||||
List<RawMaterModel> rawMaters = new List<RawMaterModel>(); | |||||
SqlHelper.GetInstance.GetRawMaterIds(res.Id).Result.OnSuccess(s => | |||||
{ | |||||
s.ForEach(item => | |||||
{ | |||||
var rmtb = RawMaters.FirstOrDefault(p => p.Id == item.RawMaterId); | |||||
if (rmtb != null) | |||||
{ | |||||
rawMaters.Add(new RawMaterModel() | |||||
{ | |||||
Weight = item.Weight, | |||||
DeviceNum = rmtb.DeviceNum, | |||||
Id = rmtb.Id, | |||||
LastModified = rmtb.LastModified, | |||||
Name = rmtb.Name, | |||||
WarehouseNum = rmtb.WarehouseNum, | |||||
}); | |||||
} | |||||
}); | |||||
}); | |||||
for (int i = 0; i < res.Count; i++) | |||||
{ | |||||
List<RawMaterModel> temp = new List<RawMaterModel>(); | |||||
rawMaters.ForEach(item => | |||||
{ | |||||
temp.Add(new RawMaterModel() | |||||
{ | |||||
Weight = item.Weight, | |||||
WarehouseNum = item.WarehouseNum, | |||||
Name = item.Name, | |||||
LastModified = item.LastModified, | |||||
Id = item.Id, | |||||
DeviceNum = item.DeviceNum | |||||
}); | |||||
}); | |||||
global.GoodsModels.Enqueue(new GoodsModel() { RawMaters = temp }); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -1,4 +1,5 @@ | |||||
using System; | |||||
using BPA.SingleDevice.Interface; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Text; | using System.Text; | ||||
@@ -8,5 +9,57 @@ namespace BPA.SingleDevice.ViewModel | |||||
{ | { | ||||
public class ParamsSetViewModel:NotifyBase | public class ParamsSetViewModel:NotifyBase | ||||
{ | { | ||||
public ParamsSetViewModel(IProcessControl processControl) | |||||
{ | |||||
WriteParamsCommand = new(() => | |||||
{ | |||||
//数据验证。 | |||||
if (ValidateData()) | |||||
{ | |||||
var isSuccess= processControl?.Conveyer.SetMoveParam(MoveSpeed, AccTime, MoveLength); | |||||
if (isSuccess is not null && isSuccess==true) | |||||
{ | |||||
Message.SuccessGlobal("写入流水线参数成功。"); | |||||
} | |||||
else | |||||
{ | |||||
Message.ErrorGlobal("写入流水线参数失败,请检查后重试。"); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
Message.ErrorGlobal("写入错误,数据验证失败。"); | |||||
} | |||||
}); | |||||
} | |||||
private bool ValidateData() | |||||
{ | |||||
return true; | |||||
} | |||||
/// <summary> | |||||
/// 流水线移动速度。 | |||||
/// </summary> | |||||
public int MoveSpeed { get; set; } | |||||
/// <summary> | |||||
/// 加速时间 | |||||
/// </summary> | |||||
public int AccTime { get; set; } | |||||
/// <summary> | |||||
/// 移动一次的距离。 | |||||
/// </summary> | |||||
public int MoveLength { get; set; } = 2000; | |||||
/// <summary> | |||||
/// 保存设置。 | |||||
/// </summary> | |||||
public BPARelayCommand SaveCommand { get; set; } | |||||
/// <summary> | |||||
/// 向流水线写入移动参数。 | |||||
/// </summary> | |||||
public BPARelayCommand WriteParamsCommand { get; set; } | |||||
} | } | ||||
} | } |
@@ -1,6 +1,10 @@ | |||||
using System; | |||||
using BPA.Model; | |||||
using BPA.Model.Attributes; | |||||
using BPA.SingleDevice.Interface; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Reflection; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -8,6 +12,163 @@ namespace BPA.SingleDevice.ViewModel | |||||
{ | { | ||||
public class VarMonitorViewModel:NotifyBase | public class VarMonitorViewModel:NotifyBase | ||||
{ | { | ||||
private readonly GlobalData global; | |||||
public VarMonitorViewModel(GlobalData global) | |||||
{ | |||||
this.global = global; | |||||
GetMonitorData(global); | |||||
TaskManage.GetInstance.StartLong(() => | |||||
{ | |||||
UpdateValue(global); | |||||
}, "UpdateValue", true); | |||||
} | |||||
public ObservableCollection<VariableMonitor> VariableMonitors { get; set; } = new ObservableCollection<VariableMonitor>(); | |||||
/// <summary> | |||||
/// 获取监控信息 | |||||
/// </summary> | |||||
private void GetMonitorData(IStatus status) | |||||
{ | |||||
if (status == null) | |||||
return; | |||||
List<VariableMonitor> vm = new List<VariableMonitor>(); | |||||
foreach (var item in status.GetType().GetProperties()) | |||||
{ | |||||
if (item.CustomAttributes.Count() > 0) | |||||
{ | |||||
var attributeName = item.CustomAttributes.FirstOrDefault(p => p.AttributeType.Name == "VariableMonitorAttribute"); | |||||
if (attributeName == null) | |||||
return; | |||||
var plcadd = item.GetCustomAttribute<VariableMonitorAttribute>()?.PLCAddress; | |||||
var modadd = item.GetCustomAttribute<VariableMonitorAttribute>()?.ModbusTcpAddress; | |||||
var notes = item.GetCustomAttribute<VariableMonitorAttribute>()?.Notes; | |||||
if (item.PropertyType?.BaseType?.Name == "Array") | |||||
{ | |||||
if (plcadd?.Length > 0) | |||||
{ | |||||
var arrayRes = item.GetValue(status, null); | |||||
if (arrayRes != null && arrayRes is Array arr) | |||||
{ | |||||
for (int i = 0; i < arr.Length; i++) | |||||
{ | |||||
var res = vm.FirstOrDefault(p => p.VarName == $"{item.Name}_{i + 1}"); | |||||
if (res == null) | |||||
{ | |||||
string[] plc = plcadd?.Substring(1).Split('.'); | |||||
string TempPlcAddress = string.Empty; | |||||
if (plc?.Length == 2) | |||||
{ | |||||
int add = int.Parse(plc[1]); | |||||
int firstAdd = int.Parse(plc[0]); | |||||
if (add >= 0 && add < 7) | |||||
{ | |||||
add += i; | |||||
} | |||||
else if (add >= 7) | |||||
{ | |||||
add = 0; | |||||
firstAdd++; | |||||
} | |||||
plc[0] = firstAdd.ToString(); | |||||
plc[1] = add.ToString(); | |||||
TempPlcAddress = $"M{plc[0]}.{plc[1]}"; | |||||
} | |||||
vm.Add(new VariableMonitor() | |||||
{ | |||||
Id = vm.Count+1, | |||||
VarName = $"{item.Name}_{i + 1}", | |||||
Notes = $"{notes}_{i + 1}", | |||||
ModbusTcpAddress = $"{int.Parse(modadd) + i}", | |||||
PLCAddress = TempPlcAddress, | |||||
}); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
var arrayRes = item.GetValue(status, null); | |||||
if (arrayRes != null && arrayRes is Array arr) | |||||
{ | |||||
for (int i = 0; i < arr.Length; i++) | |||||
{ | |||||
var res = vm.FirstOrDefault(p => p.VarName == $"{item.Name}_{i + 1}"); | |||||
if (res == null) | |||||
{ | |||||
vm.Add(new VariableMonitor() | |||||
{ | |||||
Id = vm.Count+1, | |||||
VarName = $"{item.Name}_{i + 1}", | |||||
Notes = $"{notes}_{i + 1}", | |||||
}); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
var res = vm.FirstOrDefault(p => p.VarName == item.Name); | |||||
if (res == null) | |||||
{ | |||||
vm.Add(new VariableMonitor() | |||||
{ | |||||
Id = vm.Count+1, | |||||
VarName = item.Name, | |||||
Notes = notes, | |||||
ModbusTcpAddress = modadd, | |||||
PLCAddress = plcadd, | |||||
}); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
vm.ForEach(item => { VariableMonitors.Add(item); }); | |||||
} | |||||
public void UpdateValue(IStatus status) | |||||
{ | |||||
if (status == null) | |||||
return; | |||||
foreach (var item in status.GetType().GetProperties()) | |||||
{ | |||||
if (item.CustomAttributes.Count() > 0) | |||||
{ | |||||
if (item.PropertyType?.BaseType?.Name == "Array") | |||||
{ | |||||
var arrayRes = item.GetValue(status); | |||||
if (arrayRes != null && arrayRes is Array arr) | |||||
{ | |||||
for (int i = 0; i < arr.Length; i++) | |||||
{ | |||||
int index = Array.FindIndex(VariableMonitors.ToArray(), p => p.VarName == $"{item.Name}_{i + 1}"); | |||||
if (index >= 0 && index < VariableMonitors.Count) | |||||
{ | |||||
VariableMonitors.ElementAt(index).CurrentValue = arr.GetValue(i)?.ToString(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
int index = Array.FindIndex(VariableMonitors.ToArray(), p => p.VarName == item.Name); | |||||
if (index >= 0 && index < VariableMonitors.Count) | |||||
{ | |||||
VariableMonitors.ElementAt(index).CurrentValue = item.GetValue(status)?.ToString(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
} | } |