@@ -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> | |||
<TargetFramework>net6.0-windows</TargetFramework> | |||
@@ -6,23 +6,44 @@ using System.Threading.Tasks; | |||
using System.Collections.Concurrent; | |||
using BPA.Model.Recipe; | |||
using BPA.Model.Enums; | |||
using BPA.SingleDevice.Interface; | |||
using BPA.Model.Attributes; | |||
namespace BPA.Model | |||
{ | |||
public class GlobalData | |||
public class GlobalData: IStatus | |||
{ | |||
public ConcurrentQueue<GoodsModel> GoodsModels { get; set; } = new ConcurrentQueue<GoodsModel>(); | |||
/// <summary> | |||
/// 需要执行的配方队列。 | |||
/// </summary> | |||
public ConcurrentQueue<RecipeData> RecipeQueue { get; set; } = new(); | |||
#region 配料机 | |||
/// <summary> | |||
/// 是否配料完成。 | |||
/// </summary> | |||
[VariableMonitor("配料机完成状态")] | |||
public bool[] IsCompleted { get; set; } = new bool[4]; | |||
#endregion | |||
#region 传送带 | |||
[VariableMonitor("传送带移动状态步")] | |||
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 | |||
} | |||
} |
@@ -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> | |||
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 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 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 MoveLength { get; set; } | |||
public bool MoveComplete { get;set; } | |||
@@ -69,7 +69,7 @@ namespace BPA.SingleDevice.Business | |||
this.ID = id; | |||
} | |||
public void InchMove() | |||
public void StartInchMove() | |||
{ | |||
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() | |||
{ | |||
if (IsConnected && AllowMove) | |||
@@ -105,13 +119,13 @@ namespace BPA.SingleDevice.Business | |||
return false; | |||
} | |||
public bool SetInchParam(int isReverse, float inchSpeed) | |||
public bool SetInchParam(int isReverse, int inchSpeed) | |||
{ | |||
if (IsConnected) | |||
{ | |||
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); | |||
return result1.IsSuccess && result2.IsSuccess; | |||
} | |||
@@ -123,13 +137,13 @@ namespace BPA.SingleDevice.Business | |||
return false; | |||
} | |||
public bool SetMoveParam(float moveSpeed, int accTime, int moveLength) | |||
public bool SetMoveParam(int moveSpeed, int accTime, int moveLength) | |||
{ | |||
if (IsConnected) | |||
{ | |||
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 result3 = modbus.Write<int>("VD108".ToModbusAdd(), moveLength); | |||
return result1.IsSuccess && result2.IsSuccess && result3.IsSuccess; | |||
@@ -28,16 +28,14 @@ namespace BPA.SingleDevice.Business | |||
/// <summary> | |||
/// 键就是配料设备对应的ID。这里分别为1,2,4,5。 | |||
/// </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 GlobalData global; | |||
public IConveyer Conveyer { get ; set ; } = new Conveyer(); | |||
//RecipeData currentRecipe; | |||
//CancellationTokenSource cts; | |||
@@ -57,119 +55,99 @@ namespace BPA.SingleDevice.Business | |||
Json<ConnectConfig>.Save(); | |||
} | |||
#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(2, "192.168.6.101"); | |||
//SetBatcherComm(4, "192.168.6.102"); | |||
//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 | |||
//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(); | |||
TaskManage.GetInstance.StartLong(() => | |||
{ | |||
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; | |||
} | |||
} | |||
#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); | |||
if (recipe != null) | |||
{ | |||
currentRecipes.Add(recipe); | |||
recipe.StartTime = DateTime.Now; | |||
CurrentRecipes.Add(recipe); | |||
logService.LogRunInfo($"配方【{recipe.Name}】-- 开始执行配料。"); | |||
} | |||
} | |||
} | |||
} | |||
#endregion | |||
Batching(); | |||
RefreshData(); | |||
}, "MonitorRecipeIssue", true); | |||
} | |||
/// <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> | |||
@@ -181,13 +159,8 @@ namespace BPA.SingleDevice.Business | |||
{ | |||
#region 数据验证 | |||
//if (recipe is null) | |||
//{ | |||
// logService.LogRunInfo($"参数<recipe>值为null,配料流程结束。"); | |||
// return; | |||
//} | |||
//如果配料机里没连接该工位的配料机,则直接完成。 | |||
if (!batchers.ContainsKey(stationNum)) | |||
if (!Batchers.ContainsKey(stationNum)) | |||
{ | |||
recipe.BatchStatus[stationNum] = BatchStep.BatchCompleted; | |||
recipe.IsMakeComplete[stationNum - 1] = true; | |||
@@ -195,9 +168,9 @@ namespace BPA.SingleDevice.Business | |||
return; | |||
} | |||
#endregion | |||
//数组起始索引是0,工位起始ID是1。 | |||
if (conveyer.HaveVessel[stationNum-1]) | |||
if (Conveyer.HaveVessel[stationNum-1]) | |||
{ | |||
ushort[] materialList=new ushort[14]; | |||
//获取工位需要的配料数据。 | |||
@@ -213,9 +186,10 @@ namespace BPA.SingleDevice.Business | |||
logService.LogRunInfo($"参数<stationNum>值为[{stationNum}],该配方无该工位配料需求。"); | |||
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]) | |||
{ | |||
@@ -225,7 +199,7 @@ namespace BPA.SingleDevice.Business | |||
case BatchStep.WriteBatchParam: | |||
if (materialList is not null && materialList.Length == 14) | |||
{ | |||
if (batchers[stationNum].WriteBatchData(materialList)) | |||
if (Batchers[stationNum].WriteBatchData(materialList)) | |||
{ | |||
recipe.BatchStatus[stationNum] = BatchStep.StartBatch; | |||
logService.LogRunInfo($"配方【{recipe.Name}】写入工位【{stationNum}】的下料参数【{String.Join(',',materialList)}】成功。"); | |||
@@ -243,7 +217,7 @@ namespace BPA.SingleDevice.Business | |||
} | |||
break; | |||
case BatchStep.StartBatch: | |||
if (batchers[stationNum].StartBatching()) | |||
if (Batchers[stationNum].StartBatching()) | |||
{ | |||
recipe.BatchStatus[stationNum] = BatchStep.WaitBatchComplete; | |||
logService.LogRunInfo($"配方【{recipe.Name}】工位【{stationNum}】开始配料,等待配料完成信号上升沿。"); | |||
@@ -272,49 +246,6 @@ namespace BPA.SingleDevice.Business | |||
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> | |||
@@ -324,15 +255,16 @@ namespace BPA.SingleDevice.Business | |||
//global.MoveConveyerStep = MoveConveyerStep.WaitMove; | |||
//while (global.MoveConveyerStep != MoveConveyerStep.MoveComplete) | |||
//{ | |||
var moveCompleteTrig = RTrig.GetInstance("MoveCompleted").Start(conveyer.MoveComplete); | |||
var moveCompleteTrig = RTrig.GetInstance("MoveCompleted").Start(Conveyer.MoveComplete); | |||
switch (global.MoveConveyerStep) | |||
{ | |||
case MoveConveyerStep.WaitMove: | |||
conveyer.InitalMoveParam(); | |||
Conveyer.InitalMoveParam(); | |||
Task.Delay(500).Wait(); | |||
if (conveyer.MoveOnce()) | |||
if (Conveyer.MoveOnce()) | |||
{ | |||
logService.LogRunInfo($"控制传送带去下个工位,等待动作完成信号上升沿。"); | |||
Task.Delay(500).Wait(); | |||
global.MoveConveyerStep = MoveConveyerStep.Moveing; | |||
} | |||
else | |||
@@ -350,7 +282,7 @@ namespace BPA.SingleDevice.Business | |||
} | |||
break; | |||
case MoveConveyerStep.MoveComplete: | |||
conveyer.InitalMoveParam(); | |||
Conveyer.InitalMoveParam(); | |||
UpdateRecipe(); | |||
global.MoveConveyerStep = MoveConveyerStep.WaitMove; | |||
//不会执行该步骤,会直接跳出循环。 | |||
@@ -366,9 +298,9 @@ namespace BPA.SingleDevice.Business | |||
/// <param name="port">端口号,默认为502。</param> | |||
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 | |||
{ | |||
@@ -380,27 +312,24 @@ namespace BPA.SingleDevice.Business | |||
{ | |||
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> | |||
/// 传送带和配料机之间的信号交互。 | |||
@@ -408,14 +337,14 @@ namespace BPA.SingleDevice.Business | |||
private void InterActive() | |||
{ | |||
#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 | |||
#region 传送带 | |||
conveyer.AllowMove = /*conveyer.MoveComplete &&*/ GetBatcherAllowMove(); | |||
Conveyer.AllowMove = GetBatcherAllowMove(); | |||
#endregion | |||
} | |||
/// <summary> | |||
@@ -425,9 +354,9 @@ namespace BPA.SingleDevice.Business | |||
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) | |||
{ | |||
@@ -447,7 +376,7 @@ namespace BPA.SingleDevice.Business | |||
/// <returns></returns> | |||
private bool IsAllowConveyerMove() | |||
{ | |||
foreach (var item in currentRecipes) | |||
foreach (var item in CurrentRecipes) | |||
{ | |||
if (item.IsMakeComplete[item.CurrentStation-1]==false) | |||
{ | |||
@@ -483,17 +412,22 @@ namespace BPA.SingleDevice.Business | |||
/// 初始化配料机。 | |||
/// </summary> | |||
/// <param name="configs"></param> | |||
private void InitalBatcher(IList<BatcherConfig> configs) | |||
private async void InitalBatcher(IList<BatcherConfig> configs) | |||
{ | |||
foreach (var item in configs) | |||
{ | |||
if (item.IsConnect) | |||
{ | |||
batchers.TryAdd(item.StationID, new Batcher()); | |||
Batchers.TryAdd(item.StationID, new Batcher()); | |||
SetBatcherComm(item.StationID, item.IP, item.Port); | |||
batchers[item.StationID].Initial(); | |||
} | |||
} | |||
foreach (var batcher in Batchers.Values) | |||
{ | |||
await batcher.Initial(); | |||
} | |||
} | |||
/// <summary> | |||
@@ -501,16 +435,11 @@ namespace BPA.SingleDevice.Business | |||
/// </summary> | |||
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) | |||
{ | |||
while (!conveyer.HaveVessel[item.CurrentStation]) | |||
while (!Conveyer.HaveVessel[item.CurrentStation]) | |||
{ | |||
logService.LogRunInfo($"未检测到配方【{item.Name}】在工位【{item.CurrentStation + 1}】的容器到位信号,请检查。"); | |||
Task.Delay(3000).Wait(); | |||
@@ -524,18 +453,22 @@ namespace BPA.SingleDevice.Business | |||
/// </summary> | |||
private void Batching() | |||
{ | |||
if (currentRecipes.Count>0 && IsAllowConveyerMove()) | |||
if (CurrentRecipes.Count>0 && IsAllowConveyerMove()) | |||
{ | |||
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> | |||
float InchSpeed { get; set; } | |||
int InchSpeed { get; set; } | |||
/// <summary> | |||
/// 传动带移动速度。 | |||
/// </summary> | |||
float MoveSpeed { get; set; } | |||
int MoveSpeed { get; set; } | |||
/// <summary> | |||
/// 加速时间,一般不改动。 | |||
/// </summary> | |||
@@ -58,16 +58,20 @@ namespace BPA.SingleDevice.Interface | |||
/// </summary> | |||
void SetCommParam(int id, string ip, int port = 502); | |||
/// <summary> | |||
/// 寸动【调试状态】 | |||
/// 开始寸动【调试状态】 | |||
/// </summary> | |||
void InchMove(); | |||
void StartInchMove(); | |||
/// <summary> | |||
/// 停止寸动。 | |||
/// </summary> | |||
void StopInchMove(); | |||
/// <summary> | |||
/// 设置寸动参数 | |||
/// </summary> | |||
/// <param name="isReverse">是否反转</param> | |||
/// <param name="inchSpeed">寸动速度</param> | |||
/// <returns>设置是否成功</returns> | |||
bool SetInchParam(int isReverse,float inchSpeed); | |||
bool SetInchParam(int isReverse,int inchSpeed); | |||
/// <summary> | |||
/// 设置移动参数 | |||
/// </summary> | |||
@@ -75,7 +79,7 @@ namespace BPA.SingleDevice.Interface | |||
/// <param name="accTime">移动加速时间</param> | |||
/// <param name="moveLength">移动长度</param> | |||
/// <returns>设置是否成功</returns> | |||
bool SetMoveParam(float moveSpeed, int accTime, int moveLength); | |||
bool SetMoveParam(int moveSpeed, int accTime, int moveLength); | |||
/// <summary> | |||
/// 移动一次。 | |||
/// </summary> | |||
@@ -1,4 +1,5 @@ | |||
using System; | |||
using BPA.Model.Recipe; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
@@ -12,5 +13,9 @@ namespace BPA.SingleDevice.Interface | |||
/// 初始化即开始。 | |||
/// </summary> | |||
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" | |||
HorizontalAlignment="Right" | |||
bpa:ToggleButtonHelper.CheckedContent="调试模式" | |||
x:Name="ModeButton" | |||
Command="{Binding SwitchSystemModeCommand}" | |||
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" | |||
Content="配料模式" | |||
@@ -36,6 +37,7 @@ | |||
<StackPanel | |||
HorizontalAlignment="Stretch" | |||
VerticalAlignment="Top" | |||
IsEnabled="{Binding ElementName=ModeButton,Path=IsChecked}" | |||
Orientation="Horizontal"> | |||
<Button | |||
Width="100" | |||
@@ -51,15 +51,14 @@ | |||
Grid.Row="2" | |||
Margin="0,5,0,0" | |||
bpa:GridHelper.ColumnDefinitions="*,*"> | |||
<!--<bpa:NumericBox | |||
<bpa:NumericBox | |||
bpa:ControlHelper.FocusBorderBrush="{DynamicResource Secondary}" | |||
BorderThickness="0" | |||
IsReadOnly="True" | |||
BorderThickness="0" Width="90" | |||
Style="{StaticResource FrontBackNumericBox}" | |||
Value="{Binding Count}" />--> | |||
Value="{Binding Count}" /> | |||
<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}}" | |||
CommandParameter="{Binding Id}" | |||
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.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> | |||
</UserControl> |
@@ -6,7 +6,166 @@ | |||
xmlns:local="clr-namespace:BPA.SingleDevice.View" | |||
mc:Ignorable="d" | |||
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> | |||
</UserControl> |
@@ -1,4 +1,6 @@ | |||
using System; | |||
using BPA.SingleDevice.Interface; | |||
using BPA.SingleDevice.Services; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
@@ -8,9 +10,38 @@ namespace BPA.SingleDevice.ViewModel | |||
{ | |||
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; } | |||
@@ -21,11 +52,11 @@ namespace BPA.SingleDevice.ViewModel | |||
/// <summary> | |||
/// 开始寸动。 | |||
/// </summary> | |||
public BPARelayCommand EndMoveInchCommand { get; set; } | |||
public BPARelayCommand StopMoveInchCommand { get; set; } | |||
/// <summary> | |||
/// 寸动是否反转。 | |||
/// </summary> | |||
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 System.Net.WebSockets; | |||
using System.Windows; | |||
using Newtonsoft.Json; | |||
namespace BPA.SingleDevice.ViewModel | |||
{ | |||
@@ -37,21 +38,26 @@ namespace BPA.SingleDevice.ViewModel | |||
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()!); | |||
} | |||
else | |||
{ | |||
Message.ErrorGlobal("下发配方失败,当前有配方在执行工位【1】的配料流程,请检查后重试。"); | |||
} | |||
} | |||
else | |||
{ | |||
Message.ErrorGlobal("下发配方失败,输入的配方ID为Null或空,请联系人员检查后。"); | |||
} | |||
} | |||
else | |||
{ | |||
Message.ErrorGlobal("下发配方失败,当前有配方在执行工位【1】的配料流程,请检查后重试。"); | |||
} | |||
}); | |||
} | |||
private List<RawMaterTB> RawMaters { get; set; } = new List<RawMaterTB>(); | |||
@@ -117,9 +123,13 @@ namespace BPA.SingleDevice.ViewModel | |||
materialData.Add(device.Key, channelWeight); | |||
} | |||
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 | |||
{ | |||
@@ -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.Linq; | |||
using System.Text; | |||
@@ -8,5 +9,57 @@ namespace BPA.SingleDevice.ViewModel | |||
{ | |||
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.Linq; | |||
using System.Reflection; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
@@ -8,6 +12,163 @@ namespace BPA.SingleDevice.ViewModel | |||
{ | |||
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(); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |