首次提交:添加src文件夹代码

This commit is contained in:
2026-02-27 14:02:43 +08:00
commit d330cfbca7
4184 changed files with 5546478 additions and 0 deletions

View File

@@ -0,0 +1,190 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Interface;
using Cowain.Bake.Communication.MOM;
using Cowain.Bake.Model;
using Cowain.Bake.Model.Models;
using Opc.Ua;
using Prism.Ioc;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Unity;
using JSON = Newtonsoft.Json.JsonConvert;
using static Cowain.Bake.Common.Models.MESModel;
namespace Cowain.Bake.Main.Station
{
public class AlarmStation : IServerManager
{
readonly ConcurrentDictionary<string, bool[]> _alarmValues;
public string Name { get; set; } = "";
List<TAlarm> _allAlarms { get; set; } //ConcurrentBag //验证如果还报线程问题就用ConcurrentBag
List<TAlarmContent> _allAlarmContent { get; set; }
public IUnityContainer _unityContainer { get; set; }
List<AlertInfoModel> _arrayAlert = new List<AlertInfoModel>();
private readonly Prism.Events.IEventAggregator _eventAggregator;
readonly static object _objLock = new object();
public AlarmStation(IUnityContainer unityContainer, Prism.Events.IEventAggregator eventAggregator)
{
_unityContainer = unityContainer;
_alarmValues = new ConcurrentDictionary<string, bool[]>();
_eventAggregator = eventAggregator;
Start();
}
public void Start()
{
_allAlarms = _unityContainer.Resolve<AlarmService>().GetAllInAlarms();
_allAlarmContent = _unityContainer.Resolve<AlarmContentService>().GetAll();
foreach(var item in _allAlarms)
{
_eventAggregator.GetEvent<AlarmAddEvent>().Publish(item);//2.界面刷新,发布事件(发送消息)
}
}
//报警可能是一个数组TAlarmContent也可能是一个点(TTagList)。
public void AlarmInfo(DataValue data, Variable node)
{
if (1 == data.WrappedValue.TypeInfo.ValueRank) //数组
{
lock (_objLock)
{
AlarmArray(data, node); //会导致恢复后,一条报警信息会显示二次
}
}
else
{
LogHelper.Instance.Warn("没有单个数据报警!");
}
}
public TAlarmContent GetAlarmContent(int nodeId, int offset)
{
return _allAlarmContent.Where(m => m.Offset == offset
&& m.TagIds.Split(',').Any(x => x == nodeId.ToString())
&& !string.IsNullOrEmpty(m.Desc)).FirstOrDefault();
}
AlertInfoModel AssembleAlertModel(int code, string reset, string message)
{
return new AlertInfoModel()
{
AlertCode = code.ToString(),
AlertReset = reset,
AlertMessage = message,
};
}
//一个数组报警(订阅的是一个数组) --TAlarmContent
private void AlarmArray(DataValue data, Variable node)
{
string key = "";
_arrayAlert.Clear();
bool[] value = data.WrappedValue.Value as bool[];
if (data.WrappedValue.Value is bool[] v)
{
value = v;
}
else
{
LogHelper.Instance.Warn($"数组报警类型异常:{data.WrappedValue.Value.GetType().Name}");
return;
}
//LogHelper.Instance.Warn($"数组报警:{JSON.SerializeObject(value)}");
key = $"StationId:{node.StationId},Id:{node.Id}";
if (_alarmValues.TryGetValue(key, out bool[] oldValue)) //有
{
if (oldValue.Count() != value.Count())
{
LogHelper.Instance.Error($"报警老值与新值长度不匹配key:{key},desc:{node.VarDesc}");
return;
}
var diffs = value.Zip(oldValue, (a, b) => a != b).Select((b, i) => new { Value = b, Index = i }).Where(x => x.Value);
foreach (var item in diffs)
{
DealAlarmNode(node, item.Index, value[item.Index]);
}
}
else //第一次进入
{
for (int i = 0; i < value.Count(); ++i)
{
DealAlarmNode(node, i, value[i]);
}
}
_alarmValues[key] = value;
if (int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.MOMEnable.ToString())) == (int)EMOMEnable.Enable)
{
_unityContainer.Resolve<MESProcess>().MESEqptAlert(_arrayAlert);
}
}
void DealAlarmNode(Variable node, int offset, bool value)
{
TAlarmContent alarmContent = GetAlarmContent(node.Id, offset); //找到报警信息
if (alarmContent == null)
{
return;
}
//报警三种处理1入库2.上传MOM3.界面显示报警
if (value)
{
AddAlarm(offset, node.StationId, alarmContent.Desc);
}
else
{
DeleteAlarm(offset, node.StationId, alarmContent.Desc);
}
}
public void AddAlarm(int errCode, int stationId, string desc)
{
var alarm = _allAlarms.Find(x => x.StationId == stationId && x.Desc == desc);
if (null == alarm)
{
TAlarm currentAlarm = new TAlarm()
{
StationId = stationId,
Desc = desc,
StartTime = DateTime.Now,
Status = EAlarmStatus.Alert.GetDescription(),
};
_allAlarms.Add(currentAlarm); //增加到内存
_arrayAlert.Add(AssembleAlertModel(errCode, ((int)EAlarmStatus.Alert).ToString(), desc)); //上传到MES
_unityContainer.Resolve<AlarmService>().Insert<TAlarm>(currentAlarm); //增加到DB
_eventAggregator.GetEvent<AlarmAddEvent>().Publish(currentAlarm);//.界面刷新,发布事件(发送消息)
}
}
public void DeleteAlarm(int errCode, int stationId, string desc)
{
TAlarm alarm = _allAlarms.Find(x => x.StationId == stationId
&& x.Desc == desc);
if (null != alarm)
{
_allAlarms.Remove(alarm);
_arrayAlert.Add(AssembleAlertModel(errCode, ((int)EAlarmStatus.Renew).ToString(), desc));
_unityContainer.Resolve<AlarmService>().CancelAlarm(alarm);
_eventAggregator.GetEvent<AlarmCancelEvent>().Publish(alarm);
}
}
public void Stop()
{
}
}
}

View File

@@ -0,0 +1,307 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Interface;
using Cowain.Bake.Communication.Interface;
using Cowain.Bake.Main.Common;
using Cowain.Bake.Model.Models;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Unity;
namespace Cowain.Bake.Main.Station
{
public class BakingStation : IServerManager
{
public string Name { get; set; }
readonly StoveProcessParam processParam = null;
public IUnityContainer _unityContainer { get; set; }
readonly CmdFactories cmdFactories;
readonly CancellationTokenSource cts = new CancellationTokenSource();
public BakingStation(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
processParam = _unityContainer.Resolve<StoveProcessParam>();
cmdFactories = _unityContainer.Resolve<CmdFactories>();
Start();
}
public void Start()
{
Task.Run(async () =>
{
while (!cts.Token.IsCancellationRequested)
{
try
{
if (Global.AppExit)
{
return;
}
await Task.Delay(6 * 1000);
List<ExCavityInfoModel> details = _unityContainer.Resolve<CavityInfoService>().GetAllExInfo().Where(
x => x.Type == (int)EStationType.Stove
&& x.Enable == true
&& x.CavityEnable == true).ToList();
//ProcessEndBaking(details);
ProcessStartBaking(details);
}
catch (Exception ex)
{
LogHelper.Instance.Error("BakingStation:Start:工艺参数,异常" + ex.ToString());
_unityContainer.Resolve<LogService>().AddLog("BakingStation:Start:工艺参数,异常" + ex.Message, E_LogType.Debug.ToString());
//throw;
}
}
}, cts.Token);
}
void ProcessStartBaking(List<ExCavityInfoModel> stoveDetails)
{
List<bool> bakeFlag = new List<bool>();
bool[] bakeEnable = new bool[Global.STOVE_MAX_LAYERS];
List<ExCavityInfoModel> layers = null;
var stoves = stoveDetails.GroupBy(p => new { p.StationId }).Select(s => s.FirstOrDefault()).ToList();
foreach (var stove in stoves) //烘烤以层为单位,所以以层为单位循环,这样简单
{
var conf = _unityContainer.Resolve<DeviceConfigService>().GetConfig(stove.StationId);
IPLCDevice plc = _unityContainer.Resolve<IPLCDevice>(conf.Name);
if (null == plc || !plc.IsConnect)
{
continue;
}
//远程/本地模式 //0 / 空闲 1 / 待机 2 / 停止 3 / 工作 4 / 保压
if (!CommonFun.Instance.IsStoveQualified(plc, stove.StationId, 1, false))//工作当中就退出
{
continue;
}
layers = stoveDetails.Where(x => x.StationId == stove.StationId).OrderBy(x => x.Layer).ToList();//一炉子的夹具信息
bakeFlag.Clear();
//一层的夹具信息
foreach (var detail in layers)
{
//bakeFlag = new bool[layers.Count]; //每一层有多少个可以烘烤的层
if (0 == detail.PalletId)
{
continue;
}
if (0 != detail.PalletId && !detail.IsLoad) //有托盘,无信号
{
LogHelper.Instance.Error($"有夹具,却没有信号或者没有读到信号,machineId:{stove.StationId},layer:{stove.Layer}");
continue;
}
if (detail.PalletStatus != (int)EPalletStatus.Advisable
&& detail.PalletStatus != (int)EPalletStatus.TestNG)
{
continue;
}
bakeEnable[detail.Layer] = true;
//if (detail.LastFlag ?? false)
//{
// bakeFlag = Enumerable.Repeat(true, layers.Count).ToArray();
// //break; //为了bakeEnable所以不退出
//}
bakeFlag.Add(true);
//bakeFlag[detail.Layer - 1] = true;
}
if (0 == bakeFlag.Count)
{
continue;
}
//因为当每个夹具都放水的时候,可能存在一个要复烘,一个不需要,这个时候就有为false
if (bakeFlag.Count != layers.Count // (bakeFlag.Contains(false))
&& null == layers.Find(x => x.LastFlag == true)) //不是最后一盘
{
//复烘逻辑:1.有NG2.没锁3.无有其它盘
var failPallet = layers.Where(x => x.PalletStatus == (int)EPalletStatus.TestNG).ToList();
if (0 == failPallet.Count) //没有测试NG的
{
continue;
}
if (layers.Any(x => x.Lock)) //有锁,也就是还有回炉的夹具
{
continue;
}
int countPallets = layers.Where(x => x.PalletId != 0 && x.IsLoad == true && x.PalletStatus != (int)EPalletStatus.BlankOver).Count();
if (failPallet.Count != countPallets)//有其它状态盘,如烘烤完成,也不开始烘烤
{
continue;
}
//复烘逻辑满足:1.有NG2.没锁3.无有其它状态盘
}
//if (!IsCanBaking(plc, stove.StationId, stove.Layer))
//{
// continue;
//}
#region
//清料时,可能为会空,
ExCavityInfoModel palletProceParam = stoveDetails.Where(x => !string.IsNullOrEmpty(x.JobNum)
&& x.PalletId != 0 && x.StationId == stove.StationId).FirstOrDefault();
if (!processParam.SendStoveProcessParam(plc, palletProceParam)) //成功了要改变夹具状态 EPalletStatus.
{
continue;
}
//启动烤炉自动流程/下发工艺参数完成信号(开始烘烤)
if (!cmdFactories.StartBaking(plc, stove.StationId, bakeEnable))
{
continue;
}
//去复位“烘烤完成”
cmdFactories.ResetEndBaking(stove.StationId);
//托盘绑定烤箱位置
List<int> ids = layers.Select(x => x.PalletId).ToList(); //修改夹具状态
LogHelper.Instance.Info($"下发工艺参数成功!工站:{stove.Name},工单:{stove.JobNum},托盘号:{JsonSerializer.Serialize(ids)}");
int updateResult = _unityContainer.Resolve<PalletInfoService>().UpdateStartBakingInfo(
layers.Where(x => x.PalletStatus == (int)EPalletStatus.Advisable
|| x.PalletStatus == (int)EPalletStatus.TestNG).ToList()); //托盘绑定烤箱位置,状态,时间
if (updateResult <= 0)
{
LogHelper.Instance.Warn($"修改夹具{string.Join(",", ids)}状态失败!");
}
#endregion
}
}
bool IsCanBaking(IPLCDevice plc, int mainCode, int layer)
{
return true;
}
public void TrigEndBaking(DataValue data, Variable node)
{
if (!((bool)data.WrappedValue.Value))
{
return;
}
List<ExCavityInfoModel> stoveCavitys = _unityContainer.Resolve<CavityInfoService>().GetAllExInfo().Where(
x => x.Type == (int)EStationType.Stove
&& x.Enable == true
&& x.CavityEnable == true
&& x.StationId == node.StationId).ToList();
if (0 == stoveCavitys.Count)
{
LogHelper.Instance.Error($"{node.StationId}#炉没有可能的腔体,烘烤完成失效!");
return;
}
int[] ids = stoveCavitys.Where(x =>
x.PalletStatus == (int)EPalletStatus.Bake
&& x.PalletId != 0).Select(x => x.PalletId).ToArray(); //修改夹具状态,BlankOver清料时会有这状态
if (0 == ids.Count())
{
LogHelper.Instance.Error($"{node.StationId}#炉没有找到【烘烤中】的腔体,烘烤完成失效!");
return;
}
int updateResult = _unityContainer.Resolve<PalletInfoService>().UpdateBakingOverTime(ids);
if (updateResult <= 0)
{
LogHelper.Instance.Warn($"修改夹具为烘烤完成,{string.Join(",", ids)}状态失败!");
};
}
//void ProcessEndBaking(List<ExCavityInfoModel> stoveDetails)
//{
// bool[] bakeFlag = new bool[Global.STOVE_LAYERS];
// List<ExCavityInfoModel> layers = null;
// var stoves = stoveDetails.GroupBy(p => new { p.StationId }).Select(s => s.FirstOrDefault()).ToList();
// foreach (var stove in stoves) //烘烤以炉子为单位,所以以炉子为单位循环,这样简单
// {
// Array.Clear(bakeFlag, 0, Global.STOVE_LAYERS);
// var conf = _unityContainer.Resolve<DeviceConfigService>().GetConfig(stove.StationId);
// IPLCDevice plc = _unityContainer.Resolve<IPLCDevice>(conf.Name);
// if (null == plc || !plc.IsConnect)
// {
// continue;
// }
// //远程/本地模式 //0 / 空闲 1 / 待机 2 / 停止 3 / 工作 4 / 保压
// if (!CommonFun.Instance.IsStoveQualified(plc, stove.StationId, 1, false)) //工作当中就退出
// {
// continue;
// }
// //烘烤完成信号
// var resultBool = plc.GetValue<bool>(EStoveSignal.BakingMark.ToString(), stove.StationId); //stove.Layer
// if (!resultBool.IsSuccess || !resultBool.Content)
// {
// continue;
// }
// layers = stoveDetails.Where(x => x.StationId == stove.StationId).OrderBy(x => x.Layer).ToList();//一个炉子的夹具信息
// foreach (var detail in layers)
// {
// //只有在烘烤中的,才会有烘烤完成
// if (detail.PalletStatus != (int)EPalletStatus.Bake //detail.PalletStatus != (int)EPalletStatus.Advisable
// || 0 == detail.PalletId) //detail.PalletStatus != (int)EPalletStatus.Bake
// {
// continue;
// }
// if (detail.LastFlag ?? false)
// {
// bakeFlag = Enumerable.Repeat(true, Global.STOVE_LAYERS).ToArray();
// break;
// }
// bakeFlag[detail.Number - 1] = true;
// }
// if (bakeFlag.Contains(true)) //有假表示有夹具。
// {
// int[] ids = layers.Where(x =>
// x.PalletStatus == (int)EPalletStatus.Bake
// && x.PalletId != 0).Select(x => x.PalletId).ToArray(); //修改夹具状态,BlankOver清料时会有这状态
// int updateResult = _unityContainer.Resolve<PalletInfoService>().UpdateBakingOverTime(ids);
// if (updateResult <= 0)
// {
// LogHelper.Instance.Warn($"修改夹具{string.Join(",", ids)}状态失败!");
// };
// }
// }
//}
public void Stop()
{
// 在需要取消任务的时候,调用以下代码:
cts.Cancel();
}
}
}

View File

@@ -0,0 +1,312 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Interface;
using Cowain.Bake.Communication.Interface;
using Cowain.Bake.Communication.MOM;
using Cowain.Bake.Main.Common;
using Cowain.Bake.Main.ViewModels;
using Cowain.Bake.Model;
using Cowain.Bake.Model.Models;
using HslCommunication;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Unity;
namespace Cowain.Bake.Main.Station
{
public class DataCollectStation : IServerManager
{
public string Name { get; set; }
public IUnityContainer _unityContainer { get; set; }
readonly List<TDeviceConfig> stoveConfs = null;
readonly CancellationTokenSource cts = new CancellationTokenSource();
readonly List<TStoveSctualPatrol> listStoveTemp = new List<TStoveSctualPatrol>();
private Dictionary<int, StoveDataModel> cacheStoveData { set; get; } = new Dictionary<int, StoveDataModel>();//如果只有一个保存炉子的真空值,数据容易不及时,且冲突
//private Dictionary<int, int> cacheElectricEnergyData { set; get; } = new Dictionary<int, int>();
private readonly Prism.Events.IEventAggregator _eventAggregator;
public DataCollectStation(IUnityContainer unityContainer, Prism.Events.IEventAggregator eventAggregator)
{
_unityContainer = unityContainer;
_eventAggregator = eventAggregator;
stoveConfs = _unityContainer.Resolve<DeviceConfigService>().GetConfig(EDeviceType.PLC, EStationType.Stove.ToString());//
Start();
}
public void Start()
{
string tempCycle = _unityContainer.Resolve<Cowain.Bake.BLL.SysSetupService>()
.GetValueByParaID(Cowain.Bake.Common.Enums.ESysSetup.DataCollectionCycle.ToString());
int tempCycleValue = int.Parse(tempCycle);
Task.Run(async () =>
{
await Task.Delay(20 * 1000); //等待去读PLC的数据
while (!cts.Token.IsCancellationRequested)
{
await Task.Delay(tempCycleValue * 1000);
if (Global.AppExit)
{
return;
}
CollectTemp();
}
});
}
void CollectTemp()
{
bool isWork = false;
float vacuum = 0;
UInt16 workTime = 0;
UInt16 totalWorkTime = 0;
float[] temps = null;
OperateResult<Int16[]> resultTemp;
listStoveTemp.Clear();
cacheStoveData.Clear();
foreach (var conf in stoveConfs)
{
IPLCDevice plc = _unityContainer.Resolve<IPLCDevice>(conf.Name);
if (null == plc || !plc.IsConnect)
{
continue;
}
var stoves = (from v in plc.Storages
group v by v.StationId
into a
orderby a.Key
select a).ToList();
//每个炉子都循环
foreach (var item in stoves) //一个PLC控制二台炉子(一台炉子)
{
//0 / 空闲 1 / 待机 2 / 停止 3 / 工作 4 / 保压
var workStatus = plc.GetValue<Int16>(EStoveSignal.CavityStatus.ToString(), item.Key);
if (!workStatus.IsSuccess || (int)EStoveWorkMode.Standby >= workStatus.Content) //系统状态寄存器
{
isWork = false;
}
else
{
isWork = true;
}
if (item.Key == Global.DEW_STOVE_NUMBER) //IP:70
{
//露点温度
var dewTemp = plc.GetValue<float>(EStoveSignal.DewTemperature.ToString(), item.Key);
if (!dewTemp.IsSuccess)
{
LogHelper.Instance.Error($"读取露点温度失败!{dewTemp.Message}");
_unityContainer.Resolve<BasicInfoViewModel>().SetTempData("-0");
}
else
{
DealDewTemp(dewTemp.Content);
_unityContainer.Resolve<BasicInfoViewModel>().SetTempData(Math.Round(dewTemp.Content,2).ToString());
}
}
//真空值
var result = plc.GetValue<int>(EStoveSignal.Vacuum.ToString(), item.Key);
if (!result.IsSuccess)
{
LogHelper.Instance.Error($"读取真空值失败!{result.Message}");
continue;
}
vacuum = (float)(result.Content);
//工作时长
var workTimeResult = plc.GetValue<UInt16>(EStoveSignal.WorkTime.ToString(), item.Key);
var totalTimeResult = plc.GetValue<UInt16>(EStoveSignal.TotalWorkTime.ToString(), item.Key);
if (!workTimeResult.IsSuccess
|| !totalTimeResult.IsSuccess)
{
LogHelper.Instance.Error($"读取工作时长失败!{workTimeResult.Message},{totalTimeResult.Message}");
continue;
}
workTime = workTimeResult.Content;
totalWorkTime = totalTimeResult.Content;
//获取一个炉子的温度节点
var stoveTempNode = (from storage in plc.Storages
from itemTemp in storage.VariableList
where storage.StationId == item.Key
&& itemTemp.TagType == (int)ETagType.Temperature //&& itemTemp.Number == layer
select itemTemp).ToList();
foreach (var tempParam in stoveTempNode) //每一层的温度
{
int layer = tempParam.Number;
int cavityId = _unityContainer.Resolve<CavityInfoService>().GetCavityId(item.Key, layer);
TPalletInfo palletInfo = _unityContainer.Resolve<PalletInfoService>().GetPalletCode(cavityId);
if (null == palletInfo
|| 0 == palletInfo.Id) //没有托盘
{
continue;
}
if (palletInfo.BakingBeginTime == null //没有烘烤开始时间的不用上传
|| 0 == palletInfo.VirtualId) //没有绑定电芯
{
continue;
}
//PID
var resultPID = plc.GetValue<float[]>(EStoveSignal.PID.ToString() + layer, item.Key, layer);
if (!resultPID.IsSuccess)
{
LogHelper.Instance.GetCurrentClassError("读取PID失败");
continue;
}
//温度
resultTemp = plc.GetValue<Int16[]>(tempParam.ParamName, item.Key, layer); //温度
if (!resultTemp.IsSuccess)
{
LogHelper.Instance.GetCurrentClassError("读取温度失败!");
continue;
}
if (resultTemp.Content.All(x => x == 0)
|| resultPID.Content.All(x => x == 0))
{
//LogHelper.Instance.GetCurrentClassError("读取温度异常全为0");
continue;
}
temps = CommonFun.Instance.UShortToFloat(resultTemp.Content);
cacheStoveData[cavityId] = new StoveDataModel() //少了两个条件1.VirtualId为02.工作状态
{
Temps = temps,
Vacuum = vacuum,
WorkTime = workTime,
TotalWorkTime = totalWorkTime
};
if (isWork) //add by lsm 20250926, 测试用
{
string tempData = MakeJsonTemperature(tempParam.VarDesc, temps);
string pid = MakeJsonPid(EStoveSignal.PID.ToString(), resultPID.Content);
listStoveTemp.Add(MakeTStoveSctualPatrol(palletInfo.VirtualId, cavityId, palletInfo.PalletCode, vacuum, tempData, pid));
}
}
}
}
//UploadProcessData(cacheStoveData); //1.上传Mom //太多,太卡,不上传
_eventAggregator.GetEvent<StoveDataEvent>().Publish(cacheStoveData);//2.界面刷新,发布事件(发送消息)
if (0 != listStoveTemp.Count)
{
_unityContainer.Resolve<StoveSctualPatrolService>().Insert(listStoveTemp);//批量插入数据
}
}
static bool? failDev;
void DealDewTemp(float value)
{
float target = float.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.DewTempAlarmTargetValue.ToString()));
TAlarm alarm = new TAlarm()
{
StationId = (int)EAlarmStationId.DevTemp,
Desc = "露点温度超标,请检查压缩空气!",
StartTime = DateTime.Now,
Status = EAlarmStatus.Alert.GetDescription(),
};
if (value <= target) //正常
{
if (false != failDev)
{
failDev = false;
_eventAggregator.GetEvent<AlarmCancelEvent>().Publish(alarm);
}
}
else //异常
{
if (true != failDev)
{
failDev = true;
_eventAggregator.GetEvent<AlarmAddEvent>().Publish(alarm);
}
}
}
//上转Mom
public void UploadProcessData(Dictionary<int, StoveDataModel> datas)
{
if (int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.MOMEnable.ToString())) == (int)EMOMEnable.Enable)
{
foreach (var item in datas)
{
string cavityName = _unityContainer.Resolve<MemoryDataProvider>().CavityInfo.Find(x => x.Id == item.Key).Name;
_unityContainer.Resolve<MESProcess>().MESBakingParameter(item.Value.Vacuum, item.Value.Temps, cavityName);
}
}
}
TStoveSctualPatrol MakeTStoveSctualPatrol(int palletVirtualId, int cavityId, string palletCode, float vacuum, string temperature,string pid)
{
return new TStoveSctualPatrol()
{
PalletVirtualId = palletVirtualId,
CavityId = cavityId,
PalletCode = palletCode,
Vacuum = vacuum,
Temperature = temperature,
CreateTime = DateTime.Now,
PID = pid
};
}
public string MakeJsonTemperature(string headName, float[] value)
{
headName = Regex.Replace(headName, @"\d", "");
List<PallletTemp> listTemp = new List<PallletTemp>();
for (int i = 0; i < value.Length; i++)
{
listTemp.Add(new PallletTemp()
{
HeadName = $"{headName}{i + 1}",
Value = value[i]
});
}
return JsonConvert.SerializeObject(listTemp);
}
public string MakeJsonPid(string headName, float[] value)
{
//headName = Regex.Replace(headName, @"\d", "");
List<PallletTemp> listTemp = new List<PallletTemp>();
for (int i = 1; i < value.Length; i++) //数组0不使用
{
listTemp.Add(new PallletTemp()
{
HeadName = $"{headName}{i}",
Value = value[i]
});
}
return JsonConvert.SerializeObject(listTemp);
}
public void Stop()
{
// 在需要取消任务的时候,调用以下代码:
cts.Cancel();
}
}
}

View File

@@ -0,0 +1,215 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Interface;
using Cowain.Bake.Communication.Interface;
using Cowain.Bake.Model;
using Cowain.Bake.Model.Models;
using HslCommunication;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Unity;
namespace Cowain.Bake.Main.Station
{
public class LifeCycleStation : IServerManager
{
public string Name { get; set; }
public List<TStation> _station = null;
public IUnityContainer _unityContainer { get; set; }
public LifeCycleStation(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
_station = _unityContainer.Resolve<MemoryDataProvider>().AllStation;
}
// 心跳
public void LifeCycle(DataValue data, Variable node)
{
OperateResult writeResult = null;
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
Int16 value = 0;
if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int16)
{
value = (System.Int16)data.WrappedValue.Value;
}
else
{
LogHelper.Instance.Warn($"没有找到生命周期信号这个数据类型,节点名为{node.VarDesc}");
}
var config = _unityContainer.Resolve<DeviceConfigService>().GetConfig(node.StationId);
var plc = _unityContainer.Resolve<IPLCDevice>(config.Name);
writeResult = plc.Write<Int16>((string)d.WriteHeart, value); //回复为接收到的计数
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"LifeCycle-{node.StationId}-{value}-{(string)d.WriteHeart}:{writeResult.Message}");
}
}
//向上位机请求任务,PLC请求命令:0=无意义10=无托盘20=有托盘
public void ReqTask(DataValue data, Variable node)
{
//bool IsAccord = false; //是否符合取放盘
try
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
Int16 value = 0;
if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int16)
{
value = (System.Int16)data.WrappedValue.Value;
}
else
{
LogHelper.Instance.Warn($"没有找到请求任务信号这个数据类型,节点名为{node.VarDesc}");
return;
}
TStation station = _station.Where(p => p.Id == node.StationId).FirstOrDefault();
LogHelper.Instance.Warn($"取放盘,值:{value},station:{node.StationId},层{node.Number}");
//是否符合取放盘
//if (_unityContainer.Resolve<CavityInfoService>().IsAccordReq(station, node.Number, (sbyte)value))
{
//IsAccord = true;
//修改取放盘状态
if (!_unityContainer.Resolve<CavityInfoService>().UpdateReqStatus(station, node.Number, (sbyte)value))
{
LogHelper.Instance.GetCurrentClassError($"修改请求任务失败,StationId:{node.StationId},Number:{node.Number},value:{value}");
return;
}
}
//else
//{
// LogHelper.Instance.GetCurrentClassError($"不符合取放盘,StationId:{node.StationId},Number:{node.Number},value:{value}");
//}
//DealPalletRequest(value, node); //取消这个,不然上位机启动,会报异常
var config = _unityContainer.Resolve<DeviceConfigService>().GetConfig(node.StationId);
var plc = _unityContainer.Resolve<IPLCDevice>(config.Name);
var writeResult = plc.Write<int>((string)d.WriteRetCommand, value); //收到什么反馈什么。
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"ReqTask-{(string)d.WriteRetCommand}:{writeResult.Message}");
}
value = (short)(value == 0 ? -1 : 1); //1=OK,2=重新上传如果PLC请求命令是0WCS返回-1
writeResult = plc.Write<int>((string)d.WriteResult, value); //收到什么反馈什么。
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"ReqTask-{(string)d.WriteResult}:{writeResult.Message}");
}
}
catch(Exception ex)
{
LogHelper.Instance.Error($"请求解析出错:{node.Json},{node.TrigJson},{ex.Message}");
}
}
void DealPalletRequest(int value, Variable node)
{
switch (value)
{
case (int)ECavityStatus.RequestPick:
DealPick(node);
break;
case (int)ECavityStatus.RequestPlace:
//DealPlace(station, json);
break;
case (int)ECavityStatus.None:
break;
default:
break;
}
}
public void DealPlace(TStation station, string json)
{
}
public void DealPick(Variable node)
{
MethodInfo mi = null;
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.TrigJson);
string service = d.ReflexService;
string func = d.ReflexFunc;
string param = d.Param; //方法的参数,如果是扫码,则是扫码的编号
if (string.IsNullOrEmpty(service))
{
return;
}
LogHelper.Instance.Info($"请求取盘触发事件:{node.TrigJson}");
var instnce = _unityContainer.Resolve<TrigStation>();
Type type = Type.GetType(MyPath.SIGNAL_TRIGGER + service);
mi = this.GetType().GetMethod(ReflexFun.TRIG_REPLY).MakeGenericMethod(new Type[] { type }); //回复信息;
mi.Invoke(this, new object[]
{
func,
1,
param,
node
});
}
public void TrigReply<T>(string func, int curValue, string param, Variable node)
{
//取得实例
var instnce = _unityContainer.Resolve<T>(); //上下料实例
Type t = instnce.GetType();
//取得方法
MethodInfo mi = t.GetMethod(func);
//调用方法
mi.Invoke(instnce, new object[]
{
curValue,
param,
node
});
}
//心跳
public void Alive(DataValue data, Variable node)
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
Int16 value = 0;
if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int16)
{
value = (System.Int16)data.WrappedValue.Value;
}
else
{
LogHelper.Instance.Warn($"收到心跳解析出错,工站:{node.StationId},节点名为{node.VarDesc}");
return;
}
var config = _unityContainer.Resolve<DeviceConfigService>().GetConfig(node.StationId);
var plc = _unityContainer.Resolve<IPLCDevice>(config.Name);
var writeResult = plc.Write<int>(d.RetCommand, value); //收到什么反馈什么。
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"Alive-{d.WriteResult}:{writeResult.Message}");
}
}
public void Start()
{
}
public void Stop()
{
}
}
}

View File

@@ -0,0 +1,553 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Interface;
using Cowain.Bake.Communication.Interface;
using Cowain.Bake.Communication.MOM;
using Cowain.Bake.Main.ViewModels;
using Cowain.Bake.Main.Views;
using Cowain.Bake.Model;
using Cowain.Bake.Model.Models;
using Cowain.Bake.UI.CsvMap;
using HslCommunication;
using Newtonsoft.Json;
using Prism.Ioc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using Unity;
using static Cowain.Bake.Common.Models.MESModel;
using JSON = Newtonsoft.Json.JsonConvert;
namespace Cowain.Bake.Main.Station
{
public class LoadingStation : IServerManager
{
private int _batteryCodeLen = 0;
public string Name { get; set; }
IPLCDevice PLC { get; set; }
readonly List<IScanCodeBase> _deviceScann = new List<IScanCodeBase>();
public IUnityContainer _unityContainer { get; set; }
public LoadingStation(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
TDeviceConfig config = _unityContainer.Resolve<DeviceConfigService>().GetConfig(EDeviceType.PLC, EDeviceName.Loading)[0];
Name = config.Name;
SetBatteryCodeLen();
Start();
}
public void SetBatteryCodeLen()
{
_batteryCodeLen = int.Parse(_unityContainer.Resolve<Cowain.Bake.BLL.SysSetupService>().GetValueByParaID(ESysSetup.BatteryCodeLen.ToString()));
}
bool IsConnectPLC()
{
if (null == PLC || !PLC.IsConnect)
{
LogHelper.Instance.Error($"PalletVirtualId:{PLC.Name},PLC为空!");
return false;
}
return true;
}
public void Start()
{
var configScann = _unityContainer.Resolve<DeviceConfigService>().GetConfig(EDeviceType.SCANNER).OrderBy(x => x.Id).Take((int)EScanCode.PalletScan2).ToList();
foreach (var item in configScann)
{
IScanCodeBase device = _unityContainer.Resolve<IScanCodeBase>(item.Name);
_deviceScann.Add(device);
}
PLC = _unityContainer.Resolve<IPLCDevice>(Name); //上料PLC
}
public void Stop()
{
}
/*
* 编译器自动在方法入口/出口加 lock(this),简单但锁粒度粗;
* 高频调用或重入场景容易成瓶颈,只适合做原型或低并发工具类。
*/
//托盘满盘信号
[MethodImpl(MethodImplOptions.Synchronized)]
public void ExecuteFullPallet(int curValue, string param, Variable node)
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
LogHelper.Instance.Warn($"LoadingStation:ExecuteFullPallet:开始,线程ID:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
int result ;
int batteryQty;
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("开始:" + node.VarDesc);
int palletId = _unityContainer.Resolve<CavityInfoService>().GetPalletId((int)EStationType.Loading, int.Parse(param));
TPalletInfo palletInfo = _unityContainer.Resolve<BLL.PalletInfoService>().GetPalletInfo(palletId);
if (null == palletInfo) //如果当前夹具状态为"满夹具可取",就退出,上位机重启会重复触发!
{
LogHelper.Instance.Warn($"托盘满信号,上料平台无夹具,异常");
return;
}
if ((int)EPalletStatus.Advisable == palletInfo.PalletStatus) //如果当前夹具状态为"满夹具可取",就退出,上位机重启会重复触发!
{
//LogHelper.Instance.Warn($"托盘满信号,当前托盘为满夹具夹,异常");
//return;
}
try
{
int debugMode = int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.DebugMode.ToString())); //0
if (!IsConnectPLC()) return;
var resultBatterys = PLC.Read<int[]>((string)d.ReadVirtualIds); //可以改成直接读PLC
var resultPalletVirtualId = PLC.Read<int>((string)d.ReadPalletVirtualId); //这是直接读PLC
var lastFlag = PLC.Read<Int16>((string)d.ReadLastFlag).Content; //这是直接读PLC
if (!resultBatterys.IsSuccess || !resultPalletVirtualId.IsSuccess)
{
LogHelper.Instance.Error($"PalletVirtualId:{resultPalletVirtualId.Content},没有读取电芯信息或托盘信息!");
return;
}
if (0 == resultPalletVirtualId.Content ||
1 == resultPalletVirtualId.Content)
{
LogHelper.Instance.Error($"托盘满盘信号异常, 托盘虚拟码为【{resultPalletVirtualId.Content}】!", true);
return;
}
//批量更新电芯表
batteryQty = _unityContainer.Resolve<BatteryInfoService>().InsertBatch(resultBatterys.Content, resultPalletVirtualId.Content); //add by lsm 20250219 要300耗秒以上
SavePalletInfo(resultPalletVirtualId.Content);
if (0 == batteryQty)
{
//HandyControl.Controls.MessageBox.Error("触发满盘时没有电芯数据!"); //启动上位机时,会触发这个东西。
LogHelper.Instance.Error($"启动上位机时,上料:{resultPalletVirtualId.Content},电芯地址里没有电芯数据!");
return;
}
if (Global.PALLET_TOTAL_BATTERYS != batteryQty)
{
LogHelper.Instance.Error($"PalletVirtualId:{resultPalletVirtualId.Content},{batteryQty},上料电芯是{Global.PALLET_TOTAL_BATTERYS}!", false);
}
result = _unityContainer.Resolve<PalletInfoService>().LoadingUpdatePalletStatus(resultPalletVirtualId.Content, (int)EPalletStatus.Advisable, batteryQty, lastFlag == 1);
if (0 == result)
{
LogHelper.Instance.Error($"PalletVirtualId:{resultPalletVirtualId.Content},修改托盘状态失败!");
}
//writeResult = PLC.Write<bool>((string)d.WriteResult, true);//回复收到托盘1满盘信号
//if (!writeResult.IsSuccess)
//{
// LogHelper.Instance.GetCurrentClassWarn($"{(string)d.WriteResult}:{writeResult.Message}");
//}
}
catch(Exception ex)
{
LogHelper.Instance.GetCurrentClassWarn($",{ex.Message}:{ex.StackTrace}");
}
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("结束:" + node.VarDesc);
}
//假电芯扫码 验证数据库OK
public void ExecuteScanDummy(int curValue, string param, Variable node)
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
if (!IsConnectPLC()) return;
int virtualId;
OperateResult writeResult = null;
EScanCode scanName = EScanCode.DummyScan; //(EScanCode)(int.Parse(param));
IScanCodeBase scanDevice = _deviceScann.Find(x => x.Name == scanName.ToString());
HslCommunication.OperateResult<string> scanResult = new HslCommunication.OperateResult<string>()
{
IsSuccess = false,
};
scanResult.IsSuccess = true;
scanResult.Content = System.Guid.NewGuid().ToString("N").Substring(0, 12);
if (scanResult.IsSuccess)
{
virtualId = _unityContainer.Resolve<BatteryInfoService>().InsertBattery(scanResult.Content, (int)EBatteryStatus.ScanOver, (int)EDummyState.Have, "");
writeResult = PLC.Write<int>((string)d.WriteVirtualId, virtualId); //假电芯虚拟码
if (!writeResult.IsSuccess)
{
LogHelper.Instance.GetCurrentClassWarn($"写假电芯虚拟码失败,{(string)d.WriteVirtualId}:{writeResult.Message}");
}
}
writeResult = PLC.Write<Int16>((string)d.WriteResult, scanResult.IsSuccess ? (Int16)EResult.OK : (Int16)EResult.NG); //假电芯结果
if (!writeResult.IsSuccess)
{
LogHelper.Instance.GetCurrentClassWarn($"{(string)d.WriteResult}:{writeResult.Message}");
}
writeResult = PLC.Write<Int16>((string)d.WriteAsk, 1); //回复
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"ExecuteScanDummy-{(string)d.WriteAsk}:{writeResult.Message}");
}
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("结束:" + node.VarDesc);
}
//托盘扫码
public void ExecuteScanPallet(int curValue, string param, Variable node)
{
string msg = "";
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
_unityContainer.Resolve<LogService>().AddLog("LoadingStation:ExecuteScanPallet:托盘扫码开始", E_LogType.Info.ToString());
string palletCode = "";
int VID = 0;
OperateResult writeResult = null;
HslCommunication.OperateResult<string> scanResult = new HslCommunication.OperateResult<string>()
{
IsSuccess = false,
};
EScanCode scanName = (EScanCode)((int)EScanCode.PalletScan1 + int.Parse(param) - 1); //param这个就是扫码枪的编号
try
{
IScanCodeBase scanDevice = _deviceScann.Find(x => x.Name == scanName.ToString());
while (!scanDevice.IsConnect)
{
msg = $"获取{scanName.GetDescription()}连接异常!";
LogHelper.Instance.Error(msg);
_unityContainer.Resolve<MainWindowViewModel>().AddPromptContent(msg);
scanDevice.Connect();
}
scanResult = scanDevice.ReadCode();
////------------------------ //add by lsm
//scanResult.IsSuccess = true;
//scanResult.Content = System.Guid.NewGuid().ToString("N").Substring(0, 6); // add by lsm测试用
//------------------------
palletCode = scanResult.Content;
//夹具扫码异常处理
while (string.IsNullOrEmpty(palletCode))
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
PalletIdInputWindow f = new PalletIdInputWindow("托盘码输入框", "请输入托盘码", "确定输入托盘码?")
{
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen,
};
f.ShowDialog();
palletCode = f.PalletId;
}));
}
//绑定托盘
VID = _unityContainer.Resolve<PalletInfoService>().BindingPallet(palletCode, (int)EPalletStatus.Loading, (int)EStationType.Loading, int.Parse(param)); //防呆
if (0 == VID)
{
LogHelper.Instance.Debug($"===ProcBindPallet卡死,{palletCode},{int.Parse(param)},VID:{VID}");
return; //此时会卡死上位机
}
LogHelper.Instance.Debug($"===ProcBindPallet,{palletCode},{(int)EPalletStatus.Loading},{int.Parse(param)},VID:{VID}");
if (CurrentWeaterPallet())
{
Int16 DummyLocationX = Int16.Parse(_unityContainer.Resolve<Cowain.Bake.BLL.SysSetupService>().GetValueByParaID(ESysSetup.DummyLocationX.ToString()));
writeResult = PLC.Write<Int16>((string)d.WriteDummyX, DummyLocationX); //假电芯行号
if (!writeResult.IsSuccess)
{
LogHelper.Instance.GetCurrentClassWarn($"假电芯行号:{(string)d.WriteDummyX}:{writeResult.Message},失败");
}
Int16 DummyLocationY = Int16.Parse(_unityContainer.Resolve<Cowain.Bake.BLL.SysSetupService>().GetValueByParaID(ESysSetup.DummyLocationY.ToString()));
writeResult = PLC.Write<Int16>((string)d.WriteDummyY, DummyLocationY); //假电芯列号
if (!writeResult.IsSuccess)
{
LogHelper.Instance.GetCurrentClassWarn($"假电芯列号:{(string)d.WriteDummyY}:{writeResult.Message},失败");
}
}
writeResult = PLC.Write<int>((string)d.WritePattleVirtualId, VID); //托盘1托盘虚拟码
if (!writeResult.IsSuccess)
{
LogHelper.Instance.GetCurrentClassWarn($"{(string)d.WritePattleVirtualId}:{writeResult.Message}");
}
writeResult = PLC.Write<Int16>((string)d.WriteResult, 1); //托盘1扫码结果
if (!writeResult.IsSuccess)
{
LogHelper.Instance.GetCurrentClassWarn($"{(string)d.WriteResult}:{writeResult.Message}");
}
writeResult = PLC.Write<Int16>((string)d.WriteAsk, 1); //回复
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"ExecuteScanPallet-{(string)d.WriteAsk}:{writeResult.Message}");
}
}
catch(Exception ex)
{
LogHelper.Instance.GetCurrentClassWarn($"ExecuteScanPallet:{ex},{scanResult.Content},{scanResult.Message}");
}
_unityContainer.Resolve<MainWindowViewModel>().DeletePromptContent();
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("结束:" + node.VarDesc);
}
/// <summary>
/// 根据工单判断是否放水含量
/// </summary>
/// <returns></returns>
public bool CurrentWeaterPallet()
{
bool IsWaertPallet = false;
var productionInformation = _unityContainer.Resolve<ProductionInformationService>().GetCurrentProductInfo();
if (productionInformation.DummyRule == (int)DummyPlaceRule.DEFAULT_EVERY_STOVE_LAYER_PLACED_ONE)
{
if (SettingProvider.Instance.WaterPallet % 2 == 1)
{
IsWaertPallet = true;
}
SettingProvider.Instance.WaterPallet++;
}
else if (productionInformation.DummyRule == (int)DummyPlaceRule.EVERY_PALLET_PLACED_ONE)
{
IsWaertPallet = true;
}
return IsWaertPallet;
}
//电芯扫码
public async Task ExecutScanBattery(int curValue, string param, Variable node)
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
if (!IsConnectPLC()) return;
int scannIndex = 0;
OperateResult writeResult = null;
int mesIndex = 0;
int index = 1; //1..4,第0个索引不用
string[] codeBatterys = new string[Global.ONCE_SCAN_BATTERY + 1];
Int16[] result = new Int16[Global.ONCE_SCAN_BATTERY + 1];
Int16[] ask = new Int16[Global.ONCE_SCAN_BATTERY + 1];
int[] batteryVirtualIds = new int[Global.ONCE_SCAN_BATTERY + 1];
result = Enumerable.Repeat<Int16>(3, Global.ONCE_SCAN_BATTERY + 1).ToArray();
Array.Clear(ask, 0, Global.ONCE_SCAN_BATTERY + 1); //3:无电芯
Array.Clear(batteryVirtualIds, 0, Global.ONCE_SCAN_BATTERY + 1);
List<OperateResult<string>> scannList = new List<OperateResult<string>>();
List<MESReturnCmdModel> mesResultList = new List<MESReturnCmdModel>();
List<Task<OperateResult<string>>> scannTasks = new List<Task<OperateResult<string>>>();
List<Task<MESReturnCmdModel>> mesResultTasks = new List<Task<MESReturnCmdModel>>();
int mesEnable = int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.MOMEnable.ToString()));
int debugMode = int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.DebugMode.ToString())); //0为测试模式1为正式模式
Array.Clear(codeBatterys, 0, Global.ONCE_SCAN_BATTERY + 1); //几个条码一起保存
HslCommunication.OperateResult<string> scanResult = new HslCommunication.OperateResult<string>()
{
IsSuccess = false,
};
if (mesEnable == (int)EMOMEnable.Enable
&& (int)EProductionMode.Debug == debugMode)
{
LogHelper.Instance.Error($"MOM【在线模式】设备模式却是【调试模式】模式不匹配,请修改!", true);
return;
}
try
{
index = 1;
for (EScanCode s = EScanCode.LoadingBatteryScan1; s <= EScanCode.LoadingBatteryScan8; s++, index++)
{
if (((curValue >> index) & 1) != 1) //第index位不是1,就是不需要扫码
{
continue;
}
ask[(int)s] = 1;
IScanCodeBase scanDevice = _deviceScann.Find(x => x.Name == s.ToString());
if (!scanDevice.IsConnect)
{
string msg = $"获取{s.GetDescription()}失败,请检测线路!";
LogHelper.Instance.Error(msg, true); //弹屏,人为处理好后,再次触发
_unityContainer.Resolve<MainWindowViewModel>().AddPromptContent(msg);
scanDevice.Connect();
--s; //再次获取
continue;
}
if ((int)EProductionMode.Debug == debugMode) //调试模式
{
scanResult.IsSuccess = true;
scanResult.Content = System.Guid.NewGuid().ToString("N").Substring(0, _batteryCodeLen);
scannList.Add(scanResult);
}
else
{
//scanResult = scanDevice.ReadCode();
scannTasks.Add(Task.Run(() => scanDevice.ReadCode())); // 启动并行任务
}
}
if ((int)EProductionMode.Normal == debugMode)
{
await Task.WhenAll(scannTasks); //等待所有任务(扫码)完成
foreach (var task in scannTasks)
{
scannList.Add(task.Result);
}
}
#region MOM
if (mesEnable == (int)EMOMEnable.Enable)//生产模式才会请求MES(不是离线就要请求)
{
foreach (var item in scannList) //四个电芯扫码结果
{
if (item.IsSuccess) //可能是三个扫码OK一个扫码NG所以只有三个电芯请求到MOM
{
mesResultTasks.Add(Task.Run(() => _unityContainer.Resolve<MESProcess>().GetBatteryStatus(item.Content))); //多线程入库,会有先后顺序的
}
}
if (0 != mesResultTasks.Count)
{
MESReturnCmdModel[] tt = await Task.WhenAll(mesResultTasks); // //发送MES等待所有的MES请求。
foreach (var item in mesResultTasks) //发送MOM不一定是四个
{
mesResultList.Add(item.Result);
}
}
}
#endregion
index = 1; //1..4,第0个索引不用
for (EScanCode s = EScanCode.LoadingBatteryScan1; s <= EScanCode.LoadingBatteryScan8; s++, index++)
{
if (((curValue >> index) & 1) != 1) //第index位不是1,就是不需要扫码
{
continue;
}
scanResult = scannList[scannIndex++];
result[index] = scanResult.IsSuccess ? (Int16)EResult.OK : (Int16)EResult.NG;
if (scanResult.IsSuccess) //扫码成功
{
//长度判断
//if (_batteryCodeLen != scanResult.Content.Length)
//{
// result[index] = (Int32)EResult.NG; //长度不对赋NG
// _unityContainer.Resolve<BatteryNGService>().Insert("入站拦截", scanResult.Content, $"要求电芯条码长度为{_batteryCodeLen},实际长度为:{scanResult.Content.Length}");
// continue;
//}
//0:正常扫码1:复投扫码
if ("0" == _unityContainer.Resolve<Cowain.Bake.BLL.SysSetupService>().GetValueByParaID(Cowain.Bake.Common.Enums.ESysSetup.ScanCodeMode.ToString()))
{
if (_unityContainer.Resolve<BatteryInfoService>().IsBatteryCodeRepeat(scanResult.Content))
{
result[index] = (Int32)EResult.NG; //有相同电芯赋NG
LogHelper.Instance.Warn($"{s.GetDescription()},电芯条码:[{scanResult.Content}]重复扫码,将会排出到NG位;"); //不要弹屏
_unityContainer.Resolve<BatteryNGService>().Insert("入站拦截", scanResult.Content, $"重复扫码,将会排出到NG位");
}
}
//MES结果判断
if (mesEnable == (int)EMOMEnable.Enable) ////mes-mom 7.电芯状态获取 只有联机才调用MES接口MES返回成功失败
{
MESReturnCmdModel mesResult = mesResultList[mesIndex++];//_unityContainer.Resolve<MESProcess>().MESCellState(scanResult.Content);
if (mesResult == null)
{
result[index] = (int)EResult.NG;
_unityContainer.Resolve<BatteryNGService>().Insert("入站拦截", scanResult.Content, "MOM返回信息为空请判断MOM是否离线未离线则是MOM回复异常");
continue;
}
if (mesResult.Info.ResultFlag != EResultFlag.OK.ToString())
{
result[index] = (int)EResult.NG;
_unityContainer.Resolve<BatteryNGService>().Insert("入站拦截", scanResult.Content, JSON.SerializeObject(mesResult));
continue;
}
}
codeBatterys[index] = scanResult.Content; //MES判断OK的电芯才放到电芯表中。
}
}
batteryVirtualIds = _unityContainer.Resolve<BatteryInfoService>().InsertBattery(string.Join(",", codeBatterys)).ToArray(); //新加的,四个一起存,并返回虚拟码
//InsertBattery//四个电芯一起存数据库
LogHelper.Instance.Info($"电芯虚拟码写入到PLC:{string.Join(",", batteryVirtualIds)}codes={string.Join(",", codeBatterys)},扫码状态:{string.Join(",", result)}");
writeResult = PLC.Write<int[]>((string)d.WriteVirtualIds, batteryVirtualIds); //电芯1...4虚拟码
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"写虚拟地址-{(string)d.WriteVirtualIds}:{writeResult.Message}");
}
//g_U01_HMI.UpLoadFromWcs.iCell_ScanCodeResults
writeResult = PLC.Write<Int16[]>((string)d.WriteResult, result); //电芯1...8扫码结果
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"ExecutScanBattery-{(string)d.WriteResult}:{writeResult.Message}");
}
writeResult = PLC.Write<Int16[]>((string)d.WriteAsk, ask); //回复
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"ExecutScanBattery-{(string)d.WriteAsk}:{writeResult.Message}");
}
_unityContainer.Resolve<MainWindowViewModel>().DeletePromptContent();
}
catch(Exception ex)
{
LogHelper.Instance.Fatal($"ExecutScanBattery-出错-param:{param}:{ex.Message},{index}");
}
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("结束:" + node.VarDesc);
}
//保存组盘信息
private void SavePalletInfo(int palletVID)
{
string dateFile;
string filePath;
try
{
List<TBatteryInfo> batterys = _unityContainer.Resolve<BatteryInfoService>().GetBatteryInfos(palletVID);
if (0 == batterys.Count)
{
return;
}
string path = _unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.DataFilePath.ToString());
path += "\\组盘";
dateFile = DateTime.Now.ToString("yyyyMMdd");
filePath = path + $"\\{dateFile}.csv";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
CSVHelper.WriteMap<TBatteryInfo, BatteryInfoMap>(batterys, filePath);
}
catch(Exception ex)
{
LogHelper.Instance.Error($"SavePalletInfo:{ex.Message},{ex.StackTrace}");
}
}
}
}

View File

@@ -0,0 +1,73 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Communication.MOM;
using Cowain.Bake.Main.ViewModels;
using Cowain.Bake.Model;
using Cowain.Bake.Model.Models;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Unity;
using JSON = Newtonsoft.Json.JsonConvert;
namespace Cowain.Bake.Main.Station
{
public class MesCollectStation
{
readonly CancellationTokenSource cts = new CancellationTokenSource();
readonly List<TDeviceConfig> stoveConfs = null;
IUnityContainer _unityContainer { get; set; }
public MesCollectStation(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
stoveConfs = _unityContainer.Resolve<DeviceConfigService>().GetConfig(EDeviceType.PLC, EDeviceName.StovePlc1);
}
/*
1 运行状态
2 待机状态
3 报警状态
4 停机状态
*/
//mes-mom 2.设备状态 只有联机才调用MES接口MOM返回成功失败
public void EqptStatus(DataValue data, Variable node)
{
Int16 value = (Int16)data.WrappedValue.Value;
List<TAlarm> models = null;
if (int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.MOMEnable.ToString())) == (int)EMOMEnable.Enable)
{
if ((int)EStatusCode.Warn == value)
{
var alarms = _unityContainer.Resolve<MainWindowViewModel>().Alarms.Where(x=>x.StationId == node.StationId).ToList();
models = JSON.DeserializeObject<List<TAlarm>>(JSON.SerializeObject(alarms));
}
//_unityContainer.Resolve<MESProcess>().MESEqptStatus((UInt16)value, models); //经常超时 2026-2-9
}
_unityContainer.Resolve<BasicInfoViewModel>().DeviceStatusName = ((EStatusCode)value).GetDescription();
}
//public string MakeJsonTemperature(string headName, float[] value)
//{
// headName = Regex.Replace(headName, @"\d", "");
// List<PallletTemp> listTemp = new List<PallletTemp>();
// for (int i = 1; i <= value.Length; i++)
// {
// listTemp.Add(new PallletTemp()
// {
// HeadName = $"{headName}{i}",
// Value = value[i - 1]
// });
// }
// return JsonConvert.SerializeObject(listTemp);
//}
public void Stop()
{
// 在需要取消任务的时候,调用以下代码:
cts.Cancel();
}
}
}

View File

@@ -0,0 +1,397 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Models;
using Cowain.Bake.Communication.Interface;
using Cowain.Bake.Model;
using Cowain.Bake.Model.Models;
using HslCommunication;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Unity;
using JSON = System.Text.Json.JsonSerializer;
namespace Cowain.Bake.Main.Station
{
public class StoveProcessParam
{
const int PROCESS_PARAM_NUM = 21;
//ProcessParamService procParamService;
public IUnityContainer _unityContainer { get; set; }
public StoveProcessParam(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
//procParamService = _unityContainer.Resolve<ProcessParamService>();
}
public bool SendStoveProcessParam(IPLCDevice plc, ExCavityInfoModel palletProceParam)
{
ProceParamList proceParam = null;
TProcessParameter proceParamModel = null;
//_unityContainer.Resolve<LogService>().AddLog("StoveProcessParam:SendStoveProcessParam:下发工艺参数开始"
// , E_LogType.Info.ToString());
OperateResult<AddrValue> operateResult = null;
Model.Models.Variable node = null;
List<AddrValue> makeAddrValues = new List<AddrValue>();
List<ProceParamList> listParams = null;
if (null == palletProceParam)
{
LogHelper.Instance.GetCurrentClassWarn("发送工艺参数时,没有获取到托盘信息!");
return false;
}
//判断是否是复烘
if (palletProceParam.PalletStatus == (int)EPalletStatus.TestNG)
{
//使用复烘工艺
proceParamModel = _unityContainer.Resolve<ProcessParamService>().GetReProcessParam(palletProceParam.JobNum);
}
else
{
proceParamModel = _unityContainer.Resolve<ProcessParamService>().GetProcessParam(palletProceParam.JobNum);
}
if (null == proceParamModel)
{
LogHelper.Instance.GetCurrentClassWarn("没有找到工艺参数列表!");
return false;
}
try
{
listParams = JSON.Deserialize<List<ProceParamList>>(proceParamModel.Parameters);
}
catch(Exception ex)
{
LogHelper.Instance.GetCurrentClassFatal(ex.Message);
return false;
}
//温度上限预警值
node = plc.GetVariable(EStoveSignal.TemperatureUpperWarnValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.TemperatureUpperWarnValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//真空上限预警值
node = plc.GetVariable(EStoveSignal.VacuumUpperWarnValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.VacuumUpperWarnValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
////参数_设定温度
node = plc.GetVariable(EStoveSignal.SetTemp.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.SetTemp.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam, 100); //乘以100设定10000表示100度
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_温度公差
node = plc.GetVariable(EStoveSignal.TemperatureTolerance.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.TemperatureTolerance.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam, 100); //乘以100设定10000
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_温度上限
node = plc.GetVariable(EStoveSignal.TemperatureLimit.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.TemperatureLimit.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam, 100); //乘以100设定10000
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_真空到达值
node = plc.GetVariable(EStoveSignal.VacuumArriveValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.VacuumArriveValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_真空上限
node = plc.GetVariable(EStoveSignal.VacuumUpValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.VacuumUpValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_真空下限
node = plc.GetVariable(EStoveSignal.VacuumDownValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.VacuumDownValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_氮气到达值
node = plc.GetVariable(EStoveSignal.NitrogenArriveValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.NitrogenArriveValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_氮气上限
node = plc.GetVariable(EStoveSignal.NitrogenUpValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.NitrogenUpValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_氮气下限
node = plc.GetVariable(EStoveSignal.NitrogenDownValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.NitrogenDownValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_常压到达值
node = plc.GetVariable(EStoveSignal.AtmosphericArriveValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.AtmosphericArriveValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_常压上限
node = plc.GetVariable(EStoveSignal.AtmosphericUpValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.AtmosphericUpValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_常压下限
node = plc.GetVariable(EStoveSignal.AtmosphericDownValue.ToString(), palletProceParam.StationId);
proceParam = listParams.Where(x => x.ParameterCode.ToLower() == EStoveSignal.AtmosphericDownValue.ToString().ToLower()).FirstOrDefault();
operateResult = SetNodeValue(node, proceParam);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_循环启动工步
node = plc.GetVariable(EStoveSignal.CycleStartStep.ToString(), palletProceParam.StationId);
operateResult = SetNodeValues(node, listParams, EStoveSignal.CycleStartStep.ToString(), false);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_循环结束工步
node = plc.GetVariable(EStoveSignal.CycleEndStep.ToString(), palletProceParam.StationId);
operateResult = SetNodeValues(node, listParams, EStoveSignal.CycleEndStep.ToString(), false);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_循环次数
node = plc.GetVariable(EStoveSignal.CycleNumber.ToString(), palletProceParam.StationId);
operateResult = SetNodeValues(node, listParams, EStoveSignal.CycleNumber.ToString(), false);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_加热启用
node = plc.GetVariable(EStoveSignal.HeatingEnabled.ToString(), palletProceParam.StationId);
operateResult = SetNodeValues(node, listParams, EStoveSignal.HeatingEnabled.ToString(), true);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_真空启用
node = plc.GetVariable(EStoveSignal.VacuumEnabled.ToString(), palletProceParam.StationId);
operateResult = SetNodeValues(node, listParams, EStoveSignal.VacuumEnabled.ToString(), true);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_氮气启用
node = plc.GetVariable(EStoveSignal.NitrogenEnabled.ToString(), palletProceParam.StationId);
operateResult = SetNodeValues(node, listParams, EStoveSignal.NitrogenEnabled.ToString(), true);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
//参数_工步时间
node = plc.GetVariable(EStoveSignal.StepWorkTime.ToString(), palletProceParam.StationId);
operateResult = SetNodeValues(node, listParams, EStoveSignal.StepWorkTime.ToString(), true);
if (operateResult.IsSuccess)
{
makeAddrValues.Add(operateResult.Content);
}
if (PROCESS_PARAM_NUM != makeAddrValues.Count)
{
LogHelper.Instance.GetCurrentClassError("发送工艺数据项不够!", true);
return false;
}
OperateResult result = plc.Writes(makeAddrValues.Select(x => x.Addr).ToArray(), makeAddrValues.Select(x => x.Value).ToArray());
if (!result.IsSuccess)
{
LogHelper.Instance.GetCurrentClassError($"发送工艺数据失败,{result.Message}");
return false;
}
_unityContainer.Resolve<LogService>().AddLog("StoveProcessParam:SendStoveProcessParam:下发工艺参数结束", E_LogType.Info.ToString());
return true;
}
OperateResult<AddrValue> SetNodeValues(Model.Models.Variable node, List<ProceParamList> proceParams, string key, bool fristNull)
{
object obj;
EDataType dt = EDataType.UINT32;
OperateResult<AddrValue> result = new OperateResult<AddrValue>() { IsSuccess = false };
AddrValue nodeValue = new AddrValue();
List<object> vaules = new List<object>();
if (fristNull)
{
vaules.Add(StringToObject("0", node.VarType));
}
foreach (var item in proceParams)
{
if (item.ParameterCode.ToLower().StartsWith(key.ToLower())) //在JOSN里面数组一定要按顺序排或者顺序乱了
{
dt = (EDataType)System.Enum.Parse(typeof(EDataType), node.VarType);
obj = StringToObject(item.TargetValue, node.VarType);
if (null == obj)
{
continue;
}
vaules.Add(obj);
}
}
if (vaules.Count == 0)
{
return result;
}
nodeValue.Addr = node.Address + node.VarName;
System.Type type = System.Type.GetType(dt.GetDescription());
MethodInfo mi = this.GetType().GetMethod(ReflexFun.OBJECT_TO_T).MakeGenericMethod(new System.Type[] { type }); //如果不转T直接object会写不进去
nodeValue.Value = mi.Invoke(this, new object[]
{
vaules
});
result.Content = nodeValue;
result.IsSuccess = true;
return result;
}
public T[] ObjectToT<T>(List<object> vaules) //where T : class
{
return vaules.OfType<T>().ToArray(); //object[]转T[]
}
object StringToObject(string value, string varType, float multiples = 1)
{
object obj = null;
EDataType dt = (EDataType)Enum.Parse(typeof(EDataType), varType);
try
{
switch (dt)
{
case EDataType.BOOL:
obj = (value == "1");
break;
case EDataType.UINT16:
obj = (UInt16)(System.Convert.ToSingle(value) * multiples);
break;
case EDataType.INT16:
obj = (Int16)(System.Convert.ToSingle(value) * multiples);
break;
case EDataType.INT32:
obj = (Int32)(System.Convert.ToSingle(value) * multiples);
break;
case EDataType.UINT32:
obj = (UInt32)(System.Convert.ToSingle(value) * multiples);
break;
case EDataType.FLOAT:
case EDataType.REAL:
obj = (float)(System.Convert.ToSingle(value) * multiples);
break;
default:
LogHelper.Instance.Fatal($"没有这种数据类型:{varType}");
break;
}
if (null == obj)
{
LogHelper.Instance.GetCurrentClassError("获取工艺数据的值为空");
}
}
catch(Exception ex)
{
LogHelper.Instance.GetCurrentClassDebug(ex.Message);
}
return obj;
}
OperateResult<AddrValue> SetNodeValue(Model.Models.Variable node, ProceParamList proceParam, float multiples = 1)
{
OperateResult<AddrValue> result = new OperateResult<AddrValue>() { IsSuccess = false};
AddrValue nodeValue = new AddrValue();
if (null == node)
{
LogHelper.Instance.GetCurrentClassError("获取节点数据失败");
return result;
}
if (null == proceParam)
{
LogHelper.Instance.GetCurrentClassError("获取工艺数据失败");
return result;
}
nodeValue.Addr = node.Address + node.VarName;
nodeValue.Value = StringToObject(proceParam.TargetValue, node.VarType, multiples);
if (null == nodeValue.Value)
{
return result;
}
result.IsSuccess = true;
result.Content = nodeValue;
return result;
}
}
}

View File

@@ -0,0 +1,582 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Interface;
using Cowain.Bake.Common.Models;
using Cowain.Bake.Communication.Interface;
using Cowain.Bake.Communication.MOM;
using Cowain.Bake.Main.ViewModels;
using Cowain.Bake.Model;
using Cowain.Bake.Model.Entity;
using Cowain.Bake.Model.Models;
using HslCommunication;
using Newtonsoft.Json;
using Opc.Ua;
using Prism.Ioc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Unity;
using JSON = Newtonsoft.Json.JsonConvert;
namespace Cowain.Bake.Main.Station
{
public class TaskStation: IServerManager
{
public string Name { get; set; }
static Int16 _lastStepId = 0;
public IUnityContainer _unityContainer { get; set; }
readonly CancellationTokenSource cts = new CancellationTokenSource();
readonly TDeviceConfig config;
IPLCDevice agv = null;
readonly List<CavityInfoModel> _stationCavity;
public List<TStation> _station = null;
public AutoResetEvent _newTaskEvent = new AutoResetEvent(true);
public TaskStation(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
_station = _unityContainer.Resolve<MemoryDataProvider>().AllStation;
config = _unityContainer.Resolve<DeviceConfigService>().GetConfig(EDeviceType.PLC, EDeviceName.AGV)[0];
_stationCavity = _unityContainer.Resolve<CavityInfoService>().GetAllStation();
Start();
StartNewTask();
}
public void Start()
{
var task = _unityContainer.Resolve<TaskRecordService>().UnexecuteTask();
if (null == task)
{
_lastStepId = (int)ETaskStep.None;
}
else
{
_lastStepId = (short)task.StepId;
}
agv = _unityContainer.Resolve<IPLCDevice>(config.Name);
//TTaskRecord task = await GenerateTask(); #测试
}
public async Task DealTask(DataValue data, Variable node)
{
bool dealResult = false; //进去这里,看会不会触发别的
LogHelper.Instance.Info($"开始任务--------------信号,线程ID:{System.Threading.Thread.CurrentThread.ManagedThreadId},{node.VarDesc}");
do
{
// 启动任务并等待结果
//dealResult = await Task.Run(() => ThreadDealTask(data, node));
dealResult = ThreadDealTask(data, node);
if (!dealResult)
{
LogHelper.Instance.Error("-------------------------处理任务异常!");
await Task.Delay(2 * 1000);
}
} while(!dealResult);
LogHelper.Instance.Info($"完成任务--------------信号,线程ID:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
}
void StartNewTask()
{
Task.Run( () =>
{
agv = _unityContainer.Resolve<IPLCDevice>(config.Name);
while (!cts.Token.IsCancellationRequested)
{
if (Global.AppExit)
{
return;
}
DealNewTask();
}
});
}
public void DealNewTask()
{
_newTaskEvent.WaitOne(10*1000);
Variable nodeTemp = (from secondaryList in agv.Storages //不能做触发,因为没有任务。要地直运行里面,来检测是否有任务
from item in secondaryList.VariableList
where item.ParamName == EAgvPLC.RetCount.ToString()
select item).FirstOrDefault();
Variable node = BasicFramework.DeepCopy<Variable>(nodeTemp);
if (null == node || !node.Quality || null == node.CurValue) //无值,退出
{
return;
}
TTaskRecord oldTask = _unityContainer.Resolve<TaskRecordService>().UnexecuteTask();
//1.有任务 + 自动 ,退出
//2.有任务 + 手动 + 任务执行中: 退出
if (null != oldTask)
{
if (oldTask.Status != (int)ETaskStatus.UnExecute)
{
return;
}
}
TaskEntity task = GetTask();
if (null == task)
{
return;
}
CommonCoreHelper.Instance.BlockTask.Add(task); //更新界面(任务未执行)
SetCount(node);
_lastStepId = (int)ETaskStep.MoveFrom; //StepId手动第一次为1自动第一次为10
if (!SendNextStep(task, node.Json, false)) //发送第一步
{
return; //发送失败
}
//一条任务第一次发任务时
if (!_unityContainer.Resolve<TaskRecordService>().ModifyTaskStatus(ETaskStatus.Executing)) //更新任务状态为执行中
{
LogHelper.Instance.Fatal("更改任务状态失败");
}
CommonCoreHelper.Instance.BlockTask.Add(task); //任务没有执行完。所以不需要更新界面
}
void SetCount(Variable node)
{
var model = agv.Read<Int16>(node.Address + node.VarName);
if (!model.IsSuccess
|| 1 != model.Content)
{
SettingProvider.Instance.CountCmd = 0; //每条任务从1开始
}
else
{
SettingProvider.Instance.CountCmd = 100;
}
}
private TaskEntity GetTask()
{
if (SettingProvider.Instance.DispMode == EDispatchMode.Auto)
{
var oldTask = _unityContainer.Resolve<TaskRecordService>().UnexecuteTask();
if (oldTask != null)
{
return GetStationManualTask(oldTask);
}
//看到过有任务还生成
return _unityContainer.Resolve<TaskRecordService>().GetTask(); //获得调度任务,并插入一条任务进任务记录表
}
else //手动从数据库获取
{
TTaskRecord manualTask = _unityContainer.Resolve<TaskRecordService>().UnexecuteTask();
if (null == manualTask)
{
return null; //没有任务(手动任务),就退出
}
return GetStationManualTask(manualTask);
}
}
/*
//1.执行中:机器人正在执行任务中, 如果上位机重启,会触发,
//2.无务任或任务执行完成:计命令计算为0或最后一步执行完成更新状态(无任务不需要),生成任务,下发下一步指令
//3.一步任务完成: 更新状态, 下发下一步指令
*/
public bool ThreadDealTask(DataValue data, Variable node)
{
string msg = "";
LogHelper.Instance.Error("开始接收调度机器人信息!");
Int16 countValue ;
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.TrigJson);
if (null == agv || !agv.IsConnect)
{
msg = "连接机器人的PLC断开!";
LogHelper.Instance.Error(msg);
_unityContainer.Resolve<MainWindowViewModel>().AddPromptContent(msg);
return false;
}
try
{
var stepValue = agv.Read<Int16>((string)d.ReadCmd); //可以改成直接读PLC [命令地址]
var resultValue = agv.Read<int>((string)d.ReadResult); //这是直接读PLC [执行命令结果]
if (!stepValue.IsSuccess || !resultValue.IsSuccess)
{
LogHelper.Instance.Error($"PLC反馈任务时读取失败!,失败原因:{stepValue.Message},{stepValue.Message}");
return false;
}
if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int16)
{
countValue = (Int16)data.WrappedValue.Value;
LogHelper.Instance.Debug($"接收任务信息:计数:{countValue},命令步:{stepValue.Content},结果:{resultValue.Content},当前命令步:{_lastStepId},当前计数:{SettingProvider.Instance.CountCmd}");
}
else
{
LogHelper.Instance.Warn($"没有找到这个数据类型,节点名为{node.VarDesc}", true);
return false;
}
if (!EnumHelper.IsDefined<ETaskStep>(stepValue.Content)) //不在范围内
{
LogHelper.Instance.Warn($"获取PLC的命令有问题值:{countValue}", true);
return false;
}
_unityContainer.Resolve<EquipmentMonitorViewModel>().SetAgvImage(resultValue.Content);
if (resultValue.Content == (int)EAgvMoveStatus.Error) // -1:表示没有完成
{
//if (countValue > SettingProvider.Instance.CountCmd) 此时上位机是1PLC是3。 如果执行下面后续PLC执行1就不会任务了
//{
// SettingProvider.Instance.CountCmd = countValue;
// LogHelper.Instance.Debug($"更改当前计数:{SettingProvider.Instance.CountCmd}");
//}
return false;
}
//1.机器人正在执行任务中, 如果上位机重启,此时又有任务
//如果:countValue=3000,Instance.CountCmd=1,时,到下面退出,也不影响。
if (countValue == SettingProvider.Instance.CountCmd - 1) //(重启上位机,任务在执行中)
{
LogHelper.Instance.Warn($"重启上位机,此时又有任务在执行中!");
return true;
}
if (_lastStepId == (int)ETaskStep.None)
{
LogHelper.Instance.Warn($"重启上位机, 没有任务在执行!");
return true;
}
//表示执行完成了,执行下一个; 或无任务
if (
countValue >= SettingProvider.Instance.CountCmd
&& stepValue.Content == _lastStepId
)
{
SettingProvider.Instance.CountCmd = countValue; //PLC跳步
TTaskRecord task = _unityContainer.Resolve<TaskRecordService>().UnexecuteTask();
if (null == task) //无任务:不需要做什么,有任务:更新状态
{
LogHelper.Instance.Fatal($"此时应该数据库中要有未执行的任务,计数:{countValue},命令:{stepValue}");
return true;
}
TaskEntity detailTask = GetStationManualTask(task); //通过任务,获取工站信息
detailTask.StepId = (sbyte)stepValue.Content;
if (!DealFinishCmd(detailTask, _lastStepId))
{
return false;
}
if (_lastStepId == (int)ETaskStep.Place //表示当前是一个任务的最后一条命令
/* || 0 == countCmd*/) //PLC重启为0一条完整的任务完成了,将获取最后一条指令
{
detailTask.StepId = (int)ETaskStep.Finish;
CommonCoreHelper.Instance.BlockTask.Add(detailTask); //机器人界面显示
CommonCoreHelper.Instance.MainViewAutoEvent.Set();
_newTaskEvent.Set();
return true; //完成了一条完整的任务,另外一个方法生成新的任务
}
//发送下一步命令
if (!SendNextStep(detailTask, node.Json))
{
return false;
}
CommonCoreHelper.Instance.BlockTask.Add(detailTask); //机器人界面显示
CommonCoreHelper.Instance.MainViewAutoEvent.Set();
LogHelper.Instance.Error("处理完接收调度机器人信息!");
}
else
{
LogHelper.Instance.Error($"接收到的信息PLC的调试任务异常,plc cmd:{stepValue},plc 计数:{countValue};\r" +
$"上位机 cmd:{_lastStepId},上位机 计数:{SettingProvider.Instance.CountCmd},正常不会执行到这里!");
}
}
catch(Exception ex)
{
LogHelper.Instance.GetCurrentClassError($"{ex.Message}-{ex.StackTrace}");
return false;
}
return true;
}
public bool SendNextStep(TaskEntity detailTask, string json, bool next = true)
{
LogHelper.Instance.Info($"发送步骤:{detailTask.StepId},命令计数:{SettingProvider.Instance.CountCmd}");
detailTask = SetNextStep(detailTask, next); //会设置CountCmd StepId
if (!SendTask(detailTask, json)) //发送任务
{
LogHelper.Instance.Error($"发送任务信息失败,{JsonConvert.SerializeObject(detailTask)}!");
return false;
}
_lastStepId = detailTask.StepId;
SettingProvider.Instance.CountCmd = detailTask.CountCmd;
LogHelper.Instance.Debug($"计数改变:{SettingProvider.Instance.CountCmd}");
return StartStep(detailTask);
}
public bool ManualTaskCmd(TTaskRecord task, short stepId) //界面上指定再发送一次时,
{
Variable nodeCountCmd = (from secondaryList in agv.Storages //不能做触发,因为没有任务。要地直运行里面,来检测是否有任务
from item in secondaryList.VariableList
where item.ParamName == EAgvPLC.RetCount.ToString()
select item).FirstOrDefault();
_lastStepId = (short)stepId;
TaskEntity detailTask = GetStationManualTask(task); //通过任务,获取工站信息
return SendNextStep(detailTask, nodeCountCmd.Json, false);
}
bool DealFinishCmd(TaskEntity detailTask, Int16 cmdValue)
{
if (0 >= _unityContainer.Resolve<TaskStepService>().UpdateEndTime(detailTask.Id, (ETaskStep)cmdValue))
{
LogHelper.Instance.Error($"修改命令结束时间失败任务ID:{detailTask.Id},命令:{_lastStepId}");
}
return UpdateFinishStep(detailTask);
}
bool StartStep(TaskEntity detailTask)
{
if (0 >= _unityContainer.Resolve<TaskStepService>().UpdateStartTime((int)detailTask.Id, detailTask.CountCmd, detailTask.StepId))
{
LogHelper.Instance.Error($"修改命令开始时间失败任务ID:{detailTask.Id},命令:{detailTask.StepId},命令计算:{detailTask.CountCmd}");
return false;
}
return true;
}
bool UpdateFinishStep(TaskEntity stationTask)
{
int result = 0;
switch (stationTask.StepId)
{
case (int)ETaskStep.MoveFrom:
//修改机器人的位置,给界面发信号
result =_unityContainer.Resolve<CavityInfoService>().SetRobotPos(stationTask.FromStationNumber);
break;
case (int)ETaskStep.Pick:
UpdateOutStoveTime(stationTask); //更改出炉时间
result = _unityContainer.Resolve<TaskRecordService>().BindPalletToRobot(); //将夹具绑定在机器人上
break;
case (int)ETaskStep.MoveTo:
result = _unityContainer.Resolve<CavityInfoService>().SetRobotPos(stationTask.ToStationNumber);
break;
case (int)ETaskStep.Place:
result = _unityContainer.Resolve<TaskRecordService>().UpdateTask(); //1.加锁解锁2.绑定解绑夹具3.更新机器人的位置,4.更新任务状态完成,
SendIntoStove(stationTask); //如果是满载夹具,要将调用烘烤进站接口
_unityContainer.Resolve<TaskRecordService>().UpdateFinishStepId(stationTask.Id); //修改任务步为完成
break;
default:
break;
}
if (result <= 0)
{
LogHelper.Instance.Error($"接到任务完成,但处理异常:{result},cmd:{stationTask.StepId}");
//return false;
}
return true;
}
void UpdateOutStoveTime(TaskEntity stationTask)
{
var cavityStove = _stationCavity.Where(x => x.Id == stationTask.Source && x.Type == (int)EStationType.Stove).FirstOrDefault();
if (null != cavityStove) //更改出炉时间
{
_unityContainer.Resolve<PalletInfoService>().UpdateOutStoveTime(cavityStove.PalletId);
}
}
void SendIntoStove(TaskEntity stationTask)
{
//if (!_unityContainer.Resolve<TaskTypeService>().IsPullInBaker(stationTask.TaskTypeId))
if (1 != stationTask.TaskTypeId)// 1: 上料满夹具->烤箱
{
return ;
}
var cavityInfo = _stationCavity.Where(x=>x.Id == stationTask.Target).FirstOrDefault();
var palletInfo = _unityContainer.Resolve<PalletInfoService>().GetPalletInfo(stationTask.PalletId);
List<string> batteryCodes = _unityContainer.Resolve<BatteryInfoService>().GetBatteryInfos(palletInfo.VirtualId).Select(x=>x.BatteryCode).ToList();
if (int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.MOMEnable.ToString())) == (int)EMOMEnable.Enable
&& 0 != batteryCodes.Count)
{
_unityContainer.Resolve<MESProcess>().MESBakingInput(cavityInfo.CavityName, palletInfo.PalletCode, batteryCodes, _unityContainer.Resolve<BasicInfoViewModel>().CurrentOperation);
}
}
public TaskEntity GetStationManualTask(TTaskRecord task)
{
TaskEntity taskEn = JSON.DeserializeObject<TaskEntity>(JSON.SerializeObject(task));
var sourceDetail = _unityContainer.Resolve<CavityInfoService>().GetStationDetailById(task.Source);
var targetDetail = _unityContainer.Resolve<CavityInfoService>().GetStationDetailById(task.Target);
var stationTask = MakeManualTask(taskEn, sourceDetail, targetDetail);
//stationTask.Copy(task, stationTask);
return stationTask;
}
TaskEntity SetNextStep(TaskEntity task, bool isNext = true)
{
sbyte nextStepId = (sbyte)_lastStepId;
if (isNext)
{
nextStepId = (sbyte)_unityContainer.Resolve<RgvActionService>().GetNext(_lastStepId); //下一步
}
task.CountCmd = SettingProvider.Instance.CountCmd + 1; //下一个计数
task.StepId = (sbyte)nextStepId;
return task;
}
public TaskEntity MakeManualTask(TaskEntity taskEn, TCavityInfo src, TCavityInfo desc)
{
taskEn.FromStationNumber = src.StationNumber;
taskEn.FromRow = src.Layer;
taskEn.FromColumn = src.Column;
taskEn.ToStationNumber = desc.StationNumber;
taskEn.ToRow = desc.Layer;
taskEn.ToColumn = desc.Column;
return taskEn;
}
public bool SendTask(TaskEntity task, string json)
{
LogHelper.Instance.Info($"发送任务开始");
List<AddrValue> makeAddrValues = new List<AddrValue>();
dynamic d = JsonConvert.DeserializeObject<dynamic>(json);
string fromStation = d.FromStation;
string fromLayer = d.FromRow;
string fromColumn = d.FromColumn;
string toStation = d.ToStation;
string toLayer = d.ToRow;
string toColumn = d.ToColumn;
string cmd = d.Cmd;
string countAddr = d.Count;
makeAddrValues.Add(new AddrValue()
{
Addr = fromStation,
Value = (int)task.FromStationNumber,
});
var from = _station.Where(p => p.Id == (int)task.FromStationNumber).FirstOrDefault();
if (from.Type == (int)EStationType.Loading
|| from.Type == (int)EStationType.UnLoading)
{
makeAddrValues.Add(new AddrValue()
{
Addr = fromLayer,
Value = (int)task.FromRow,
});
makeAddrValues.Add(new AddrValue()
{
Addr = fromColumn,
Value = (int)task.FromColumn,
});
}
else
{
makeAddrValues.Add(new AddrValue()
{
Addr = fromLayer,
Value = (int)task.FromColumn,
});
makeAddrValues.Add(new AddrValue()
{
Addr = fromColumn,
Value = (int)task.FromRow,
});
}
makeAddrValues.Add(new AddrValue()
{
Addr = toStation,
Value = (int)task.ToStationNumber,
});
var to = _station.Where(p => p.Id == (int)task.ToStationNumber).FirstOrDefault();
if (to.Type == (int)EStationType.Loading
|| to.Type == (int)EStationType.UnLoading)
{
makeAddrValues.Add(new AddrValue()
{
Addr = toLayer,
Value = (int)task.ToRow,
});
makeAddrValues.Add(new AddrValue()
{
Addr = toColumn,
Value = (int)task.ToColumn,
});
}
else
{
makeAddrValues.Add(new AddrValue()
{
Addr = toLayer,
Value = (int)task.ToColumn,
});
makeAddrValues.Add(new AddrValue()
{
Addr = toColumn,
Value = (int)task.ToRow,
});
}
makeAddrValues.Add(new AddrValue()
{
Addr = cmd,
Value = (Int16)task.StepId,
});
//makeAddrValues.Add(new AddrValue()
//{
// Addr = countAddr,
// Value = (Int16)task.CountCmd, //必须最后一个写
//});
OperateResult result = agv.Writes(makeAddrValues.Select(x => x.Addr).ToArray(), makeAddrValues.Select(x => x.Value).ToArray());
if (!result.IsSuccess)
{
LogHelper.Instance.Fatal($"写任务步骤失败:{JsonConvert.SerializeObject(makeAddrValues)},{JsonConvert.SerializeObject(makeAddrValues)}");
}
result = agv.Write<Int16>(countAddr, (Int16)task.CountCmd);
if (!result.IsSuccess)
{
LogHelper.Instance.Fatal($"写任务计数失败:{JsonConvert.SerializeObject(makeAddrValues)},{JsonConvert.SerializeObject(makeAddrValues)}");
}
LogHelper.Instance.Debug($"发送任务:{JsonConvert.SerializeObject(makeAddrValues)}");
LogHelper.Instance.Debug($"发送计数:{countAddr}:{(Int16)task.CountCmd}");
//_unityContainer.Resolve<LogService>().AddLog("TaskStation:SendTask:发送任务结束", E_LogType.Info.ToString());
return result.IsSuccess;
}
public void Stop()
{
// 在需要取消任务的时候,调用以下代码:
cts.Cancel();
}
}
}

View File

@@ -0,0 +1,225 @@
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Interface;
using Cowain.Bake.Communication.PLC;
using Cowain.Bake.Main.ViewModels;
using Cowain.Bake.Model.Models;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Unity;
namespace Cowain.Bake.Main.Station
{
public class TrigStation : ITrigService
{
IUnityContainer _unityContainer;
readonly CancellationTokenSource cts = new CancellationTokenSource();
public TrigStation(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
//怕消息先后导致异常,所以只能单个去
//for (int i = 0; i < 3; i++) //还有心跳,就放开这个功能
{
Start();
}
}
public void Start()
{
Task.Run( () =>
{
while (true)
{
if (Global.AppExit)
{
return;
}
BlockData item = _unityContainer.Resolve<PLCBlockingCollection>().MsgBlock.Take();
int msgCount = _unityContainer.Resolve<PLCBlockingCollection>().MsgBlock.Count;
if ("Heart" == item.Node.ParamName) //消息队列太多,就删除心跳
{
if (msgCount >= 5)
{
LogHelper.Instance.Error($"----------消费者消息数:{msgCount}");
continue;
}
Task.Run(() => ExecuteHeartTask(item.Data, item.Node));
}
else
{
RecvTrigInfo(item.Data, item.Node);
}
}
});
}
void ExecuteHeartTask(DataValue data, Variable node)
{
RecvTrigInfo(data, node);
}
//多线程处理
public void RecvTrigInfo(DataValue data, Variable node)
{
int value = 0;
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.TrigJson);
string service = d.Service;
string func = d.Func;
string trigValue = d.TrigValue;
string param = d.Param; //方法的参数,如果是扫码,则是扫码的编号
MethodInfo mi = null;
if (string.IsNullOrEmpty(service)
|| string.IsNullOrEmpty(func))
{
LogHelper.Instance.Error("解析触发信号信息失败!解析内容:" + node.TrigJson);
return;
}
Type type = Type.GetType(MyPath.SIGNAL_TRIGGER + service);
if (!string.IsNullOrEmpty(trigValue))
{
value = GetTrigValue(data);
if (data.Value.GetType().IsArray)
{
if (value == 0) //节点的值跟所触发的信号相同(6098,扫码是数组)
{
return;
}
}
else
{
if (value != int.Parse(trigValue)) //节点值
{
return;
}
}
//LogHelper.Instance.Info($"5--------------信号,线程ID:{System.Threading.Thread.CurrentThread.ManagedThreadId},{node.VarDesc}:{param},func:{func},value:{value},{node.Json}"); //执行到这,信号都会丢,
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("开始:" + node.VarDesc);
mi = this.GetType().GetMethod(ReflexFun.TRIG_REPLY).MakeGenericMethod(new Type[] { type }); //回复信息;
mi.Invoke(this, new object[]
{
func,
value,
param,
node
});
//_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("结束0:" + node.VarDesc);
}
else
{
mi = this.GetType().GetMethod(ReflexFun.TRIG_INSTANCE).MakeGenericMethod(new Type[] { type }); //夹具信号,报警这些;
mi.Invoke(this, new object[]
{
func,
data,
node
});
}
}
public void TrigInstance<T>(string func, DataValue data, Variable node)
{
//取得实例
var instnce = _unityContainer.Resolve<T>();
Type t = instnce.GetType();
//取得方法
MethodInfo mi = t.GetMethod(func);
//调用方法
mi.Invoke(instnce, new object[]
{
data,
node
});
}
public void TrigReply<T>(string func, int curValue, string param, Variable node)
{
//取得实例
var instnce = _unityContainer.Resolve<T>(); //上下料实例
Type t = instnce.GetType();
//取得方法
MethodInfo mi = t.GetMethod(func);
//调用方法
mi.Invoke(instnce, new object[]
{
curValue,
param,
node
});
}
private int GetTrigValue(DataValue data)
{
int value = 0;
if (data.WrappedValue.TypeInfo.ValueRank == 1) //数组
{
if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Boolean) //下料扫码
{
bool[] values = (bool[])data.Value;
for (int i = 0; i < values.Count(); i++)
{
if (values[i])
{
double temp = Math.Pow(2, i); //o
value += (int)temp;
}
}
}
else //上料扫码
{
Int16[] values = (Int16[])data.Value;
for (int i = 0; i < values.Count(); i++)
{
double temp = values[i] * Math.Pow(2, i); //ok
value += (int)temp;
}
LogHelper.Instance.Fatal($"接收扫码信号:{string.Join(",", values)},{value}");
}
}
else
{
if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.UInt16)
{
value = (UInt16)data.WrappedValue.Value;
}
else if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int16)
{
value = (Int16)data.WrappedValue.Value;
}
else if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int32)
{
value = (Int32)data.WrappedValue.Value;
}
else if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.UInt32)
{
value = (int)data.WrappedValue.Value;
}
else if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Boolean)
{
value = ((bool)data.WrappedValue.Value) ? 1 : 0;
}
else
{
value = (int)data.WrappedValue.Value;
}
}
return value;
}
}
}

View File

@@ -0,0 +1,772 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Interface;
using Cowain.Bake.Common.Models;
using Cowain.Bake.Communication.Interface;
using Cowain.Bake.Communication.MOM;
using Cowain.Bake.Main.ViewModels;
using Cowain.Bake.Main.Views;
using Cowain.Bake.Model;
using Cowain.Bake.Model.Entity;
using Cowain.Bake.Model.Models;
using Cowain.Bake.UI.CsvMap;
using HslCommunication;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Unity;
using static Cowain.Bake.Common.Models.MESModel;
using JSON = Newtonsoft.Json.JsonConvert;
namespace Cowain.Bake.Main.Station
{
public class UnLoadingStation : IServerManager
{
IPLCDevice PLC { get; set; }
const int DUMMY_POS_SIZE = 3;
private int _batteryCodeLen = 0;
List<IScanCodeBase> _deviceScann = new List<IScanCodeBase>();
List<TBatteryInfo> _tempList = new List<TBatteryInfo>();
public string Name { get; set; }
public IUnityContainer _unityContainer { get; set; }
private readonly Prism.Events.IEventAggregator _eventAggregator;
public UnLoadingStation(IUnityContainer unityContainer, Prism.Events.IEventAggregator eventAggregator)
{
_unityContainer = unityContainer;
_eventAggregator = eventAggregator;
TDeviceConfig config = _unityContainer.Resolve<DeviceConfigService>().GetConfig(EDeviceType.PLC, EDeviceName.Loading)[0]; //上下料一个PLC
Name = config.Name;
SetBatteryCodeLen();
Start();
}
bool IsConnectPLC()
{
if (null == PLC || !PLC.IsConnect)
{
LogHelper.Instance.Error($"PalletVirtualId:{PLC.Name},PLC为空!");
return false;
}
return true;
}
public void Start()
{
var configScann = _unityContainer.Resolve<DeviceConfigService>().GetConfig(EDeviceType.SCANNER).OrderByDescending(x => x.Id).Take(8).ToList();
foreach (var item in configScann)
{
IScanCodeBase device = _unityContainer.Resolve<IScanCodeBase>(item.Name);
_deviceScann.Add(device);
}
PLC = _unityContainer.Resolve<IPLCDevice>(Name); //下料PLC
System.Threading.Tasks.Task.Run(() => BuildRecordFile());
}
void BuildRecordFile()
{
string dateFile = "";
string filePath = "";
string path = _unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.DataFilePath.ToString());//
path += "\\出站";
while (true)
{
Thread.Sleep(1000 * 60);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
dateFile = DateTime.Now.ToString("yyyyMMdd");
filePath = path + $"\\{dateFile}.csv";
var batteryList = _unityContainer.Resolve<BatteryInfoService>().GetOutbound();
if (null == batteryList)
{
continue;
}
try
{
CSVHelper.WriteMap<BatteryInfoEntity, BatteryInfoDetailMap>(batteryList, filePath);
foreach (var item in batteryList)
{
if (0 == _unityContainer.Resolve<BatteryInfoService>().UpdateStatus(item.Id, (int)EBatteryStatus.OutBoundRecord))
{
LogHelper.Instance.Fatal("修改记录出站状态失败!");
}
}
}
catch (Exception ex)
{
LogHelper.Instance.Fatal($"BuildRecordFile{ex.Message}");
}
}
}
void SetDummyToWaterPlatform(string param)
{
int number = int.Parse(param);
int palletId = _unityContainer.Resolve<CavityInfoService>().GetPalletId((int)EStationType.UnLoading, number);
_unityContainer.Resolve<BLL.BatteryInfoService>().SetDummyToWaterPlatform(palletId);
}
//开始下料 ,,验证数据库OK (分二次执行,一次取假电芯,一次取正常电芯)
public void ExecuteUnLoading(int curValue, string param, Variable node)
{
//Int16 batteryType = 1; //1:假电芯, 0:正常电芯
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
if (!IsConnectPLC()) return;
try
{
if (GetUnloadBattery(param, node.Json, out bool isUnLodingDummy, out TPalletInfo palletInfo))
{
_unityContainer.Resolve<LogService>().AddLog("ExecuteUnLoading:下料写入托盘电池信息!", E_LogType.Info.ToString());
WriteBatteryPostionToPLC(palletInfo, node.Json, isUnLodingDummy);
}
else
{
//SetEmptyPallet(node.Json); //如果上料中写了下料电芯数据,此时再重启上位机,状态为“下料中”,会清除,所以注释
}
var writeResult = PLC.Write<Int16>((string)d.WriteResult, 1);
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Error($"ExecuteScanPallet-写数据失败:{(string)d.WriteResult}:{writeResult.Message}");
}
}
catch (Exception ex)
{
LogHelper.Instance.Error($"ExecuteUnLoading出错,{ex.Message},param{param},{node.Json}");
}
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("结束:" + node.VarDesc);
}
/// <summary>
/// 结果数据上传到MOM烘烤开始时间烘烤结束时间烘烤温度真空值,出炉冷却后温度
/// </summary>BakingOutputCmd,MESReturnCmdModel
/// <param name="batterys"></param>
/// <param name="palletInfo"></param>
public MESReturnCmdModel MesOutUnBinding(TPalletInfo palletInfo, List<TBatteryInfo> betterys, bool realTimeSend = false)
{
if (0 == palletInfo.BakingPosition.Value)
{
palletInfo.BakingPosition = 1; //防呆
LogHelper.Instance.Error("手动触发烘烤完成,没有烘烤位置!");
}
TCavityInfo cavityInfo = _unityContainer.Resolve<CavityInfoService>().GetStationDetailById(palletInfo.BakingPosition.Value);
if (null == cavityInfo)
{
LogHelper.Instance.GetCurrentClassError("通过烘烤位置查找托盘位置失败");
return null;
}
return _unityContainer.Resolve<MESProcess>().MESBakingOutput(cavityInfo.Name, palletInfo, betterys, true, realTimeSend);
}
//请求下料,出现异常处理
TPalletInfo DealStartUnloadFail(int number)
{
//去机器人上获取夹具信息,此时没有转移。
TPalletInfo palletInfo = _unityContainer.Resolve<BLL.PalletInfoService>().GetRobotPallet();
if (null != palletInfo)
{
LogHelper.Instance.Error("请求下料的夹具在机器人上面!");
return palletInfo;
}
//如果还没有,再到下料获取。
int palletId = _unityContainer.Resolve<CavityInfoService>().GetPalletId((int)EStationType.UnLoading, number);
return _unityContainer.Resolve<BLL.PalletInfoService>().GetPalletInfo(palletId);
}
private bool GetUnloadBattery(string param, string reply, out bool isUnLodingDummy, out TPalletInfo palletInfo)
{
isUnLodingDummy = false;
dynamic d = JsonConvert.DeserializeObject<dynamic>(reply);
OperateResult operateResultX = null, operateResultY = null;
OperateResult operateResult = null;
List<TBatteryInfo> batterys = null;
TBatteryInfo dummy;
Int16 batteryType = 1; //1:假电芯, 0:正常电芯
int number = int.Parse(param);
Int16[] dummyPos = new Int16[DUMMY_POS_SIZE];
int[] batteryVirtualId = new int[Global.PALLET_MAX_BATTERYS];
Array.Clear(batteryVirtualId, 0, Global.PALLET_MAX_BATTERYS);
int palletId = _unityContainer.Resolve<CavityInfoService>().GetPalletId((int)EStationType.UnLoading, number);
palletInfo = _unityContainer.Resolve<BLL.PalletInfoService>().GetPalletInfo(palletId);
//要求PLC必须在上位机反馈任务完成之后再请求托盘参数
if (null == palletInfo) //会出现异常(发触发下料,再告诉上位机放)
{
palletInfo = DealStartUnloadFail(number);
if (null == palletInfo) //
{
LogHelper.Instance.Error($"接收到下料信号,但获取不到下料工站{param}的夹具信息!", true);
return false;
}
}
batterys = _unityContainer.Resolve<BLL.BatteryInfoService>().GetBatteryInfos(palletInfo.VirtualId);
if (0 == batterys.Count)
{
_unityContainer.Resolve<LogService>().AddLog($"下料工站{param},找不到电池位置数据!", E_LogType.Info.ToString());
return false;
}
//要满足下料条件
if (palletInfo.PalletStatus != (int)EPalletStatus.BakeOver
&& palletInfo.PalletStatus != (int)EPalletStatus.TestOK)
{
LogHelper.Instance.Fatal($"下料夹具状态不对,请人为处理,要求状态:{EPalletStatus.BakeOver.GetDescription()},{EPalletStatus.TestOK.GetDescription()},实际状态:" +
$"{((EPalletStatus)palletInfo.PalletStatus).GetDescription()}");
return false;
}
if (palletInfo.PalletStatus == (int)EPalletStatus.BakeOver
&& _unityContainer.Resolve<BLL.BatteryInfoService>().IsPalletDummy(palletInfo.VirtualId)) //带水含量电芯,要测试
{
dummy = _unityContainer.Resolve<BLL.BatteryInfoService>().FindDummy(palletInfo, batterys);
isUnLodingDummy = true;
if (null == dummy)
{
LogHelper.Instance.Fatal("获取假电芯信息失败!");
return false;
}
dummyPos[1] = (Int16)dummy.PositionX.Value;
dummyPos[2] = (Int16)dummy.PositionY.Value;
operateResultX = PLC.Write<Int16>((string)d.WriteDummyX, dummyPos[1]); //托盘假电池行列数
operateResultY = PLC.Write<Int16>((string)d.WriteDummyY, dummyPos[2]); //托盘假电池行列数
if (!operateResultX.IsSuccess
|| !operateResultY.IsSuccess)
{
LogHelper.Instance.Debug($"设置托盘假电池行列数失败:{operateResultX.Message},{operateResultY.Message}");
return false;
}
}
else
{
batteryType = 0; //正常电芯
_unityContainer.Resolve<BLL.PalletInfoService>().UpdateUnLoadingBegingTime(palletInfo.Id);
List<TBatteryInfo> normalBatterys = (from b in batterys
where b.BatteryStatus <= (int)EBatteryStatus.ToPallet
select b).ToList();
if (Global.PALLET_ROWS < Global.PALLET_COLS) //7行24列:行少列多
{
foreach (var item in normalBatterys)
{
batteryVirtualId[(item.PositionX.Value - 1) * Global.PALLET_COLS + item.PositionY.Value] = item.Id;
}
}
else//48行,2列: 行少列多
{
foreach (var item in normalBatterys)
{
batteryVirtualId[(item.PositionY.Value - 1) * Global.PALLET_ROWS + item.PositionX.Value] = item.Id;
}
}
LogHelper.Instance.Info("下料写入电芯位置:" + JSON.SerializeObject(batteryVirtualId));
}
//水含量电芯时全为0
operateResult = PLC.Write<int[]>((string)d.WriteVirtualIds, batteryVirtualId); //托盘_有无电池[1-120]
if (!operateResult.IsSuccess)
{
LogHelper.Instance.Error($"设置托盘_有无电池失败:{operateResult.Message}");
}
//电芯类型
operateResult = PLC.Write<Int16>((string)d.WriteType, batteryType); //托盘1正常电芯下料
if (!operateResult.IsSuccess)
{
LogHelper.Instance.Error($"写下料电芯类型失败:{(string)d.WriteType},{operateResult.Message}");
return false;
}
return true;
}
//写下料位置的电池位置信息给PLC
private void WriteBatteryPostionToPLC(TPalletInfo palletInfo, string reply, bool isUnLodingDummy)
{
Int16 lastValue = 0;
dynamic d = JsonConvert.DeserializeObject<dynamic>(reply);
OperateResult operateResult = null;
////水含量电芯时全为0
//operateResult = PLC.Write<int[]>((string)d.WriteVirtualIds, batteryVirtualId); //托盘_有无电池[1-120]
//if (!operateResult.IsSuccess)
//{
// LogHelper.Instance.Error($"设置托盘_有无电池失败:{operateResult.Message}");
//}
if (!isUnLodingDummy)
{
lastValue = (Int16)(palletInfo.LastFlag ? 1 : 0);
}
operateResult = PLC.Write<Int16>((string)d.WriteLastFlag, lastValue); //最后一盘
if (!operateResult.IsSuccess)
{
LogHelper.Instance.Error($"设置托盘最后一盘失败:{operateResult.Message},{operateResult.Message}");
}
//在给下料参数成功之后,再更新托盘状态。
if (0 == _unityContainer.Resolve<BLL.PalletInfoService>().UpdatePalletStatus(palletInfo.Id, (int)EPalletStatus.Blank))
{
LogHelper.Instance.Error($"在给下料参数成功之后,再更新托盘状态:{operateResult.Message},{operateResult.Message}");
}
}
public void SetEmptyPallet(string reply)
{
_unityContainer.Resolve<LogService>().AddLog("SetEmptyPallet:设置空托盘", E_LogType.Info.ToString());
dynamic d = JsonConvert.DeserializeObject<dynamic>(reply);
List<AddrValue> makeAddrValues = new List<AddrValue>();
int[] batteryVirtualId = new int[Global.PALLET_MAX_BATTERYS];
Array.Clear(batteryVirtualId, 0, Global.PALLET_MAX_BATTERYS);
makeAddrValues.Add(new AddrValue()
{
Addr = (string)d.WriteDummyX,
Value = (Int16)0
});
makeAddrValues.Add(new AddrValue()
{
Addr = (string)d.WriteDummyY,
Value = (Int16)0
});
makeAddrValues.Add(new AddrValue()
{
Addr = (string)d.WriteType,
Value = (Int16)0
});
makeAddrValues.Add(new AddrValue()
{
Addr = (string)d.WriteVirtualIds,
Value = batteryVirtualId
});
makeAddrValues.Add(new AddrValue()
{
Addr = (string)d.WriteResult,
Value = true
});
OperateResult result = PLC.Writes(makeAddrValues.Select(x => x.Addr).ToArray(), makeAddrValues.Select(x => x.Value).ToArray());
if (!result.IsSuccess)
{
LogHelper.Instance.GetCurrentClassError("清空托盘失败!");
}
}
public void ExecuteUnLoadingFinish(int curValue, string param, Variable node)
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
//-----------------------------------------------------------------------------
int debugMode = int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.DebugMode.ToString())); //0为测试模式1为正式模式
if (!IsConnectPLC()) return;
//得到这个工站的夹具,通过夹具得到电芯
int number = int.Parse(param);
int palletId = _unityContainer.Resolve<BLL.CavityInfoService>().GetPalletId((int)EStationType.UnLoading, number);
TPalletInfo palletInfo = _unityContainer.Resolve<BLL.PalletInfoService>().GetPalletInfo(palletId);
if (null == palletInfo)
{
LogHelper.Instance.Error($"托盘为空, 下料{number}#工站,palletId:{palletId}");
return;
}
_unityContainer.Resolve<BLL.PalletInfoService>().UpdateUnLoadingOverTime(palletInfo.Id);
if (0 == palletInfo.VirtualId
|| 1 == palletInfo.Id)
{
LogHelper.Instance.Error($"下料夹具异常, 下料托盘的虚拟码为0或下料托盘为【新夹具】");
}
else
{
_unityContainer.Resolve<BLL.PalletInfoService>().PalletInfoToHistory(palletInfo.VirtualId,
(int)EPalletStatus.BlankOver, (int)EBatteryStatus.Over, (int)EStationType.UnLoading, number);
}
var writeResult = PLC.Write<Int16>((string)d.WriteResult, 1);
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"ExecuteUnLoadingFinish-{(string)d.WriteResult}:{writeResult.Message}");
}
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("结束:" + node.VarDesc);
}
/// <summary>
/// 核对电芯条码,下料扫码
/// </summary>
//void VerifyBatteryCode(int curValue, string param, string reply)
//{
// dynamic d = JsonConvert.DeserializeObject<dynamic>(reply);
// var result = DealVerify(reply);
// var writeResult = PLC.Write<bool[]>((string)d.WriteResult, result.Result);//
// if (!writeResult.IsSuccess)
// {
// LogHelper.Instance.GetCurrentClassWarn($"{(string)d.WriteResult}:{writeResult.Message}");
// }
//}
//async Task<bool[]> DealVerify(string reply)
//{
// int index = 0, j = 0;
// bool[] results = new bool[Global.ONCE_SCAN_BATTERY];
// Array.Clear(results, 0, Global.ONCE_SCAN_BATTERY);
// List<Task<OperateResult<string>>> scannTasks = new List<Task<OperateResult<string>>>();
// dynamic d = JsonConvert.DeserializeObject<dynamic>(reply);
// if (!IsConnectPLC()) return results;
// OperateResult<Int32[]> batterys = PLC.Read<Int32[]>((string)d.ReadVirtualIds); //下料处电芯虚拟码 读取下料温度[1..8]
// if (!batterys.IsSuccess)
// {
// LogHelper.Instance.Warn($"核对电芯条码时,读取电芯虚拟码错误:{batterys.Message}");
// return results;
// }
// if (batterys.Content.All(x => x == 0)) //True 全部为0
// {
// LogHelper.Instance.Warn($"核对电芯条码时读取电芯虚拟码异常全部虚拟码都为0");
// return results;
// }
// //读条码
// for (EScanCode s = EScanCode.UnLoadingBatteryScan1; s <= EScanCode.UnLoadingBatteryScan8; s++, index++)
// {
// if (0 == batterys.Content[index])
// {
// continue;
// }
// IScanCodeBase scanDevice = _deviceScann.Find(x => x.Name == s.ToString());
// if (!scanDevice.IsConnect)
// {
// string msg = $"获取{s.GetDescription()}失败,请检测线路!";
// LogHelper.Instance.Error(msg, true); //弹屏,人为处理好后,再次触发
// _unityContainer.Resolve<MainWindowViewModel>().AddPromptContent(msg);
// scanDevice.Connect();
// --s; //再次获取
// continue;
// }
// scannTasks.Add(Task.Run(() => scanDevice.ReadCode())); // 启动并行任务
// }
// await Task.WhenAll(scannTasks); //等待所有任务(扫码)完成
// index = 0;
// var batteryCodes = GetBatteryCode(batterys.Content);//获取PLC传过来的虚拟码
// //比较条码
// for (EScanCode s = EScanCode.UnLoadingBatteryScan1; s <= EScanCode.UnLoadingBatteryScan8; s++, j++)
// {
// if (0 == batterys.Content[index])
// {
// continue;
// }
// if (!batteryCodes.Contains(scannTasks[index++].Result.Content)) //没有用等于号,是因为这样兼容更好一点
// {
// LogHelper.Instance.Warn($"下料核对电芯异常PLC电芯:{string.Join(",", batteryCodes)},扫码电芯:{JSON.SerializeObject(scannTasks)}");
// //continue; //异常
// }
// else
// {
// results[j] = true;
// }
// }
// _unityContainer.Resolve<MainWindowViewModel>().DeletePromptContent();
// return results;
//}
//取假电芯完成验证数据库OK
public void ExecuteTakeDummyFinish(int curValue, string param, Variable node)
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
if (!IsConnectPLC()) return;
_unityContainer.Resolve<LogService>().AddLog("UnLoadingStation:ExecuteTakeDummyFinish:取假电池开始:" + param, E_LogType.Info.ToString());
int number = int.Parse(param);
int palletId = _unityContainer.Resolve<BLL.CavityInfoService>().GetPalletId((int)EStationType.UnLoading, number);
TPalletInfo palletInfo = _unityContainer.Resolve<BLL.PalletInfoService>().GetPalletInfo(palletId);
if (null == palletInfo)
{
LogHelper.Instance.Error($"接收下料{number}工站取假电芯完成信号,但获取不到下料夹具!", true);
return;
}
//这里再改假电芯状态
SetDummyToWaterPlatform(param);
//重启后可能再次收到1#托盘取假电池完成信号待水含量测试此时状态可能测试OK或NG
if (palletInfo.PalletStatus == (int)EPalletStatus.BakeOver
|| palletInfo.PalletStatus == (int)EPalletStatus.Blank) //取水含量电芯时,也是下料中状态
{
_unityContainer.Resolve<BLL.PalletInfoService>().UpdatePalletStatus(palletInfo.Id, (int)EPalletStatus.WaitTest);
}
var writeResult = PLC.Write<Int16>((string)d.WriteResult, 1);
if (!writeResult.IsSuccess)
{
LogHelper.Instance.Warn($"ExecuteTakeDummyFinish-{(string)d.WriteResult}:{writeResult.Message}");
}
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("结束:" + node.VarDesc);
}
public void SetBatteryCodeLen()
{
_batteryCodeLen = int.Parse(_unityContainer.Resolve<Cowain.Bake.BLL.SysSetupService>().GetValueByParaID(ESysSetup.BatteryCodeLen.ToString()));
}
public async Task<List<OperateResult<string>>> GetScannResult(int curValue)
{
int index = 1;
List<string> batteryCodes = new List<string>();
List<OperateResult<string>> scannList = new List<OperateResult<string>>();
List<Task<OperateResult<string>>> scannTasks = new List<Task<OperateResult<string>>>();
OperateResult<string> scanResult = new OperateResult<string>()
{
IsSuccess = false,
};
for (EScanCode s = EScanCode.UnLoadingBatteryScan1; s <= EScanCode.UnLoadingBatteryScan8; s++, index++)
{
if (((curValue >> index) & 1) != 1) //第index位不是1,就是不需要扫码
{
continue;
}
IScanCodeBase scanDevice = _deviceScann.Find(x => x.Name == s.ToString());
if (!scanDevice.IsConnect)
{
string msg = $"获取{s.GetDescription()}失败,请检测线路!";
LogHelper.Instance.Error(msg, true); //弹屏,人为处理好后,再次触发
_unityContainer.Resolve<MainWindowViewModel>().AddPromptContent(msg);
scanDevice.Connect();
--s; //再次获取
continue;
}
scannTasks.Add(Task.Run(() => scanDevice.ReadCode())); // 启动并行任务
}
await Task.WhenAll(scannTasks); //等待所有任务(扫码)完成
foreach (var task in scannTasks)
{
scannList.Add(task.Result);
}
return scannList;
}
public async Task<bool[]> DealTemp(IPLCDevice plc, int curValue, string reply)
{
string msg = "";
int index = 1;
int scannIndex = 0;
bool[] repResults = new bool[Global.ONCE_SCAN_BATTERY + 1];
dynamic d = JsonConvert.DeserializeObject<dynamic>(reply);
List<string> batteryCodes = null;
List<OperateResult<string>> scannList;
MESReturnCmdModel result = null;
bool isDummy = false;
Array.Clear(repResults, 0, Global.ONCE_SCAN_BATTERY + 1);
int debugMode = int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.DebugMode.ToString())); //0为测试模式1为正式模式
_tempList.Clear();
try
{
if (!IsConnectPLC()) return null;
OperateResult<float[]> readTemps = plc.Read<float[]>((string)d.ReadTemp); //下料处电芯温度,读取下料温度[1..4]
if (!readTemps.IsSuccess)
{
LogHelper.Instance.Warn("读取下料温度失败");
return null;
}
if ((int)EProductionMode.Debug == debugMode) //调试模式
{
repResults[1] = true;
return repResults;
}
scannList = await GetScannResult(curValue); //
for (EScanCode s = EScanCode.UnLoadingBatteryScan1; s <= EScanCode.UnLoadingBatteryScan8; s++, index++)
{
if (((curValue >> index) & 1) != 1) //第index位不是1,就是不需要扫码
{
continue;
}
repResults[index] = true;
if (scannList[scannIndex].IsSuccess)
{
_tempList.Add(new TBatteryInfo()
{
BatteryCode = scannList[scannIndex].Content, //实际要扫码
CoolTemp = readTemps.Content[index] //1...8排序
});
}
else
{
if (MessageBoxResult.Yes == HandyControl.Controls.MessageBox.Show($"[{s.GetDescription()}]扫码失败,您确定此位置是否有电芯?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Information))
{
_tempList.Add(new TBatteryInfo()
{
BatteryCode = GetBatteryCode(s.GetDescription()), //弹屏,输入电芯条码
CoolTemp = readTemps.Content[index] //1...8排序
});
}
else //无电芯
{
repResults[index] = false; //扫码NG并人为确认无电芯
}
}
scannIndex++;
}
batteryCodes = _tempList.Select(x => x.BatteryCode).ToList();
LogHelper.Instance.Info($"读取下料PLC的电芯虚拟码:{string.Join(",", batteryCodes)}");
List<TBatteryInfo> currentBatterys = _unityContainer.Resolve<BatteryInfoService>().GetBatterys(batteryCodes); //8个电芯(最多),可能会来自多个托盘
if (null == currentBatterys
|| currentBatterys.Count == 0) //PLC有BUG有时为0导致上位机没办法回复会PLC超时
{
msg = $"下发温度读取失败,{Global.ONCE_SCAN_BATTERY}个电池虚拟码为0!";
LogHelper.Instance.Error("下发温度读取完成失败,电池虚拟码为0!");
_unityContainer.Resolve<LogService>().AddLog("下发温度读取完成失败,电池虚拟码为0!", E_LogType.Fatal.ToString());
_unityContainer.Resolve<MainWindowViewModel>().AddPromptContent(msg);
return repResults;
}
else if (currentBatterys.Count == 1) //一个电芯,判断是否是假电芯
{
if (_unityContainer.Resolve<BatteryInfoService>().IsDummy(currentBatterys[0].Id))
{
isDummy = true;
return repResults; //假电芯就不上传了
}
}
_unityContainer.Resolve<BatteryInfoService>().UpdateCoolTempAndUnBindingTime(currentBatterys, _tempList);// temps);
if (int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.MOMEnable.ToString())) == (int)EMOMEnable.Enable)
{
var batterySort = currentBatterys.GroupBy(p => new { p.PalletVirtualId }).Select(s => s.FirstOrDefault()).ToList(); //获取一个托盘中的一个电芯
foreach (var item in batterySort) //一个托盘只返回第一个电芯
{
var palletBatterys = currentBatterys.Where(x => batteryCodes.Contains(x.BatteryCode) && x.PalletVirtualId == item.PalletVirtualId).ToList(); //获取一个托盘中的电芯
TPalletInfo palletInfo = _unityContainer.Resolve<BLL.PalletInfoService>().GetPalletInfoByBatteryVirtualId(item.PalletVirtualId); //此时托盘可能已经转历史盘托了
if (null == palletInfo)
{
msg = $"MOM上传冷却温度时,找不到电芯所在托盘,电芯:{string.Join(",", palletBatterys.Select(x => x.BatteryCode).ToList())}!";
LogHelper.Instance.Fatal(msg);
_unityContainer.Resolve<MainWindowViewModel>().AddPromptContent(msg);
return null;
}
var mesResult = MesOutUnBinding(palletInfo, palletBatterys);//组装数据 发送
//因为没有等MOM所以注掉下面代码
//if (mesResult == null)
//{
// LogHelper.Instance.Error($"自动出站MOM返回超时,电芯条码:{string.Join(",", palletBatterys.Select(x => x.BatteryCode).ToList())}"); //偶尔会返回空,导致人为要处理,麻烦,所以只保存到日志
//}
//else if (mesResult.Info.ResultFlag.ToUpper() == "NG" && (!isDummy))
//{
// msg = $"自动出站MOM返回信息异常,信息:{JSON.SerializeObject(result)}";
// LogHelper.Instance.Error(msg);
// _unityContainer.Resolve<MainWindowViewModel>().AddPromptContent(msg);
//}
//else
//{
// _unityContainer.Resolve<MainWindowViewModel>().DeletePromptContent();
//}
}
}
}
catch (Exception e)
{
LogHelper.Instance.Fatal($"读温失败:{e.Message},{e.StackTrace}");
}
return repResults;
}
public string GetBatteryCode(string desc)
{
string batteryCode = "";
while (string.IsNullOrEmpty(batteryCode))
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
PalletIdInputWindow f = new PalletIdInputWindow("电芯码输入框", $"{desc}扫码失败," + "请输入电芯条码", "确定输入电芯条码?")
{
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen,
};
f.ShowDialog();
batteryCode = f.PalletId;
}));
if (!_unityContainer.Resolve<BatteryInfoService>().IsBatteryCodeRepeat(batteryCode))
{
LogHelper.Instance.Error($"【{batteryCode}】,不存在,请重新输入!", true);
batteryCode = "";
}
}
return batteryCode;
}
//获取下料温度
public async Task GetTemp(int curValue, string param, Variable node)
{
OperateResult operateResult = null;
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.Json);
IPLCDevice plc = _unityContainer.Resolve<IPLCDevice>((string)d.PLCName); //上料PLC
var result = await DealTemp(plc, curValue, node.Json);
if (result == null)
{
return;
}
operateResult = plc.Write<bool[]>((string)d.WriteResult, result); //MOM没有返回数据导致
if (!operateResult.IsSuccess)
{
LogHelper.Instance.Error("下发温度读取完成失败!");
return;
}
_unityContainer.Resolve<BasicInfoViewModel>().SetEvent("结束:" + node.VarDesc);
}
public void Stop()
{
}
}
}

View File

@@ -0,0 +1,200 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Interface;
using Cowain.Bake.Communication.Interface;
using Cowain.Bake.Model.Models;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Concurrent;
using System.Linq;
using Unity;
namespace Cowain.Bake.Main.Station
{
public class UpdateStation : IServerManager
{
readonly ConcurrentDictionary<int, bool[]> _stovePalletSignal;
public string Name { get; set; }
public IUnityContainer _unityContainer { get; set; }
public UpdateStation(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
_stovePalletSignal = new ConcurrentDictionary<int, bool[]>();
}
bool IsConnectPLC(IPLCDevice PLC)
{
if (null == PLC || !PLC.IsConnect)
{
LogHelper.Instance.GetCurrentClassError($"连接PLC失败:{PLC.Name},PLC为空!");
return false;
}
return true;
}
//LifeCycle PLC请求命令:0=无意义10=无托盘20=有托盘
public int UpdateCavityStatus(DataValue data, Variable node)
{
Int16 signalValue = 0;
Int16 result = (Int16)EResult.OK; //1=OK,2=重新上传
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.TrigJson); //生命周期
dynamic reply = JsonConvert.DeserializeObject<dynamic>(node.Json); //反馈收到PLC的周期信号,反馈PLC的命令,0也要反馈,1=OK,2=重新上传
var config = _unityContainer.Resolve<DeviceConfigService>().GetConfig(node.StationId);
IPLCDevice PLC = _unityContainer.Resolve<IPLCDevice>(config.Name);
if (!IsConnectPLC(PLC)) return 0;
var countCavitySignal = PLC.GetValue<System.Int16>((string)d.CountCavitySignal, node.StationId, node.Number);
if (!countCavitySignal.IsSuccess)
{
result = (Int16)EResult.NG;
LogHelper.Instance.Error($"读取腔体的信号计数失败!原因:{countCavitySignal.Message}");
}
if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int16)
{
signalValue = (System.Int16)data.WrappedValue.Value;
}
else
{
result = (Int16)EResult.NG;
LogHelper.Instance.Warn($"没有找到夹具信号这个数据类型,节点名为{node.VarDesc}");
}
var writeResult = PLC.Write<Int16>(reply.CountCavitySignal, countCavitySignal.Content);
if (!writeResult.IsSuccess)
{
LogHelper.Instance.GetCurrentClassWarn($"写腔体的信号计数失败,地址:{reply.CountCavitySignal},值:{countCavitySignal.Content},{writeResult.Message}");
}
writeResult = PLC.Write<Int16>(reply.SignalValue, signalValue);
if (!writeResult.IsSuccess)
{
LogHelper.Instance.GetCurrentClassWarn($"写腔体的信号失败,地址:{reply.SignalValue},值:{signalValue},{writeResult.Message}");
}
writeResult = PLC.Write<Int16>(reply.CavitySignalResult, result);
if (!writeResult.IsSuccess)
{
LogHelper.Instance.GetCurrentClassWarn($"写回复腔体的信号失败,地址:{reply.CavitySignalResult},值:{result},{writeResult.Message}");
}
return _unityContainer.Resolve<CavityInfoService>().UpdateCavitySignal(node.StationId, node.Number, signalValue);
}
public int UpdateDoorStatus(DataValue data, Variable node)
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.TrigJson);
string layer = d.Layer;
bool status = false;
if (string.IsNullOrEmpty(layer))
{
LogHelper.Instance.Warn($"触发夹具到位信号,但获取相关信息失败{node.TrigJson}");
return 0;
}
if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Boolean)
{
status = (bool)data.WrappedValue.Value;
}
else
{
LogHelper.Instance.Warn($"没有找到开/关门这个数据类型,节点名为{node.VarDesc}");
return 0;
}
if (!status)
{
return 0;
}
if (node.ParamName == EStoveSignal.OpenDoorPlace.ToString())
{
status = true;
}
return _unityContainer.Resolve<CavityInfoService>().UpdateDoorStatus(node.StationId, int.Parse(layer), status);
}
public int UpdateLoadStatus(DataValue data, Variable node)
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(node.TrigJson);
string layer = d.Layer;
string number = d.Number;
bool[] status = null;
if (data.WrappedValue.TypeInfo.ValueRank == 1) //数组,炉子夹具传感器信号
{
if (data.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Boolean)
{
status = (bool[])data.WrappedValue.Value;
}
if (_stovePalletSignal.TryGetValue(node.StationId, out bool[] oldValue)) //有
{
if (oldValue.Count() != status.Count())
{
LogHelper.Instance.Error($"烤箱到位信号老值与新值长度不匹配,工站:{node.StationId},desc:{string.Join(",", status)}");
return 0;
}
_stovePalletSignal[node.StationId] = status;
var diffs = status.Zip(oldValue, (a, b) => a != b).Select((b, i) => new { Value = b, Index = i }).Where(x => x.Value);
foreach (var item in diffs)
{
if (0 == item.Index)
{
continue; //第一个不用
}
int result = _unityContainer.Resolve<CavityInfoService>().UpdateLoadStatus(node.StationId, item.Index, 1, status[item.Index]);
if (0 != result)
{
LogHelper.Instance.Warn($"修改状态数据失败,StationId:{node.StationId},layer:{item.Index}{status[item.Index]}");
}
}
}
else //重启时,更新所有信号
{
_stovePalletSignal[node.StationId] = status;
for (int i = 1; i <= Global.STOVE_LAYERS; ++i) //上位机启动
{
int result = _unityContainer.Resolve<CavityInfoService>().UpdateLoadStatus(node.StationId, i, 1, status[i]);
if (0 != result)
{
LogHelper.Instance.Warn($"修改状态数据失败,StationId:{node.StationId},layer:{i}{status[i]}");
}
}
}
}
else //上下料和人工夹具平台
{
var value = (bool)data.WrappedValue.Value;
int result = _unityContainer.Resolve<CavityInfoService>().UpdateLoadStatus(node.StationId, int.Parse(layer), int.Parse(number), value);
if (0 != result)
{
LogHelper.Instance.Warn($"修改状态数据失败,StationId:{node.StationId},layer:{layer}{number},{value}");
}
}
return 1;
}
public void Start()
{
}
public void Stop()
{
}
}
}

View File

@@ -0,0 +1,227 @@
using Cowain.Bake.BLL;
using Cowain.Bake.Common;
using Cowain.Bake.Common.Core;
using Cowain.Bake.Common.Enums;
using Cowain.Bake.Common.Models;
using Cowain.Bake.Model;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Unity;
using JSON = Newtonsoft.Json.JsonConvert;
using Cowain.Bake.Main.ViewModels;
using static Cowain.Bake.Common.Models.MESModel;
using Cowain.Bake.Communication.MOM;
using Cowain.Bake.Model.Models;
namespace Cowain.Bake.Main.Station
{
//自动上传模块
public class UploadMesStation
{
public bool? OrdeIsMesOnline { get; set; }
public string Name { get; set; }
public bool haveWarn = true;
public string IpAddress { get; set; }
public int Port { get; set; }
public string URL { get; set; }
public IUnityContainer _unityContainer { get; set; }
public int Id { get; set; }
List<ProceParamList> listParams = new List<ProceParamList>();
private readonly Prism.Events.IEventAggregator _eventAggregator;
public UploadMesStation(IUnityContainer unityContainer, Prism.Events.IEventAggregator eventAggregator)
{
bool IsMesOnline = false;
_unityContainer = unityContainer;
TDeviceConfig dev = _unityContainer.Resolve<DeviceConfigService>().GetConfig(EDeviceType.MOM)[0];
Id = dev.Id;
Name = dev.Name;
_eventAggregator = eventAggregator;
Task.Run(async () =>
{
while (true)
{
if (Global.AppExit)
{
return;
}
IsMesOnline = false;
if (int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.MOMEnable.ToString())) == (int)EMOMEnable.Enable)
{
MESReturnCmdModel mesResult = _unityContainer.Resolve<MESProcess>().Alive();
IsMesOnline = DealAlive(mesResult);
}
if (OrdeIsMesOnline != IsMesOnline)
{
;
OrdeIsMesOnline = IsMesOnline;
_unityContainer.Resolve<DeviceConfigService>().UpdateStatus(this.Id, IsMesOnline);
_unityContainer.Resolve<BasicInfoViewModel>().MesStatus = IsMesOnline;
}
await Task.Delay(5000); //放这里的目的,是心跳后,再执行上传
}
});
Task.Run(async () =>
{
while (true)
{
await Task.Delay(3000);
if (Global.AppExit)
{
return;
}
if (int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.MOMEnable.ToString())) == (int)EMOMEnable.Enable)
{
Run();
}
}
});
}
//MOM在回复心跳时通过KeyFlag= 2提醒设备需要调用MOM的工艺参数下发接口
//MOM在回复心跳时通过KeyFlag= 2提醒设备需要调用MOM的工艺参数下发接口
bool DealAlive(MESReturnCmdModel mesResult)
{
if (null == mesResult)
{
LogHelper.Instance.Warn($"MES不在线:{JSON.SerializeObject(mesResult)}");
return false;
}
else if (((int)EKeyFlag.NG).ToString() == mesResult.Info.KeyFlag)
{
TAlarm model = new TAlarm()
{
StationId = (int)EAlarmStationId.Mom,
Desc = $"MOM心跳报警->{mesResult.Info.MOMMessage}", //会存在多次不同的报警
StartTime = DateTime.Now,
Status = EAlarmStatus.Alert.GetDescription(),
};
haveWarn = true;
_eventAggregator.GetEvent<AlarmAddEvent>().Publish(model);//2.界面刷新,发布事件(发送消息)
_unityContainer.Resolve<AlarmService>().Insert(model);
return true;
}
else if (((int)EKeyFlag.ProcessParam).ToString() == mesResult.Info.KeyFlag) //心跳时通过KeyFlag= 2提醒设备需要调用MOM的工艺参数下发接口
{
listParams.Clear();
EqptParameterReturnCmd resultModel = _unityContainer.Resolve<MESProcess>().GetProcessParam();//获取返回的JOSN
if (null == resultModel)
{
return true;
}
TProductionInformation model = _unityContainer.Resolve<ProductionInformationService>().GetCurrentProductInfo();//实例化工单类
if (!_unityContainer.Resolve<ProcessParamService>().UpdateProcessParam(model.ProcessParamId, JSON.SerializeObject(resultModel.Info.ParameterInfo)))
{
LogHelper.Instance.Warn("保存工艺参数失败!");
}
}
if (haveWarn && ((int)EKeyFlag.NG).ToString() != mesResult.Info.KeyFlag)
{
_eventAggregator.GetEvent<AlarmStaionCancelEvent>().Publish((int)EAlarmStationId.Mom);//2.界面刷新,发布事件(发送消息)
var alarms = _unityContainer.Resolve<AlarmService>().GetInAlarm((int)EAlarmStationId.Mom);
foreach (var item in alarms)
{
_unityContainer.Resolve<AlarmService>().CancelAlarm(item);
}
haveWarn = false;
}
return true;
}
//public void GetJsonParam(string param)
//{
// dynamic d = JsonConvert.DeserializeObject<dynamic>(param);
// this.IpAddress = d.Ip;
// this.Port = d.Port;
// this.URL = d.URL;
//}
void Run()
{
if (int.Parse(_unityContainer.Resolve<SysSetupService>().GetValueByParaID(ESysSetup.MOMEnable.ToString())) != (int)EMOMEnable.Enable)
{
return;
}
List<TMesData> uploadItems = _unityContainer.Resolve<MesDataService>().GetUpLoadData();
foreach (var item in uploadItems)
{
dynamic d = JsonConvert.DeserializeObject<dynamic>(item.CommandType);
string service = d.Service;
string func = d.Func;
string url = d.UrlCmd;
Type type = Type.GetType(MyPath.SIGNAL_TRIGGER + service);
MethodInfo mi = this.GetType().GetMethod("ExecuteUpload").MakeGenericMethod(new Type[] { type }); //回复信息;
mi.Invoke(this, new object[]
{
func,
url,
item,
});
}
}
public void ExecuteUpload<T>(string func, string url, TMesData mesModel)
{
//取得实例
var instnce = _unityContainer.Resolve<T>(); //上下料实例
Type t = instnce.GetType();
//取得方法
MethodInfo mi = t.GetMethod(func);
//调用方法
mi.Invoke(instnce, new object[] //这里会去执行哪个方法上传,参数只有发送的信息
{
url,
mesModel
});
}
public void UploadCommon(string url, TMesData mesModel)
{
try
{
//这里就是调用上传的接口
mesModel.SendTime = DateTime.Now;
string result = HttpClientHelper.HttpPost(url, mesModel.Content);
if (string.IsNullOrEmpty(result)) //请求失败
{
mesModel.SendFlag = (sbyte)EMesUpLoadStatus.Fail;
}
else //请求成功
{
mesModel.SendFlag = (sbyte)EMesUpLoadStatus.Success;
mesModel.RecvContent = result;
mesModel.RecvTime = DateTime.Now;
_unityContainer.Resolve<MESProcess>().WriteRequestLog(mesModel.Content);
_unityContainer.Resolve<MESProcess>().WriteResponseLog(result);
}
_unityContainer.Resolve<MesDataService>().Update(mesModel);
}
catch (Exception ex)
{
LogHelper.Instance.GetCurrentClassError(ex.Message);
}
}
}
}