using CMS.Extensions.Variable;
using CMS.Plugin.FlowManagement.Abstractions.FlowBusiness;
using CMS.Plugin.PipeLineLems.Apis;
using CMS.Plugin.PipeLineLems.Application.Contracts.Dtos.MyTestEntityNames;
using CMS.Plugin.PipeLineLems.Domain.MyTestEntityNames;
using CMS.Plugin.PipeLineLems.Jobs;
using CMS.Plugin.ProcessManagement.Abstractions;
using CMS.Plugin.TraceManagement.Abstractions.Models.Traces;
using CMS.Plugin.TraceManagement.Abstractions;
using CMS.Project;
using CMS.Project.Abstractions;
using CMS.Unit.RuntimeValue.Abstractions;
using KissUtil.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Uow;
using CMS.Plugin.PipeLineLems.Application.Contracts.Services;
using CMS.Plugin.PipeLineLems.Domain.WorkPlan;
using CMS.Plugin.PipeLineLems.Domain.CallMaterialOrder;
namespace CMS.Plugin.PipeLineLems.ProjectService
{
///
/// 工程服务,和工程关联的后台服务,当以当前Key调用时会被执行
///
public class PipeLineLemsProjectService : BaseProjectService
{
private IServiceProvider _serviceProvider;
private readonly ILogger _logger;
private readonly IVariableDataCache _variableDataCache;
///
/// 变量服务
///
private readonly VariableService _variableService;
private FlowVariableChannelListener _channelListener;
private Dictionary _monitorVariableNames;
///
/// 服务的Key,唯一,供使用
///
public override string Key => "PipeLineLems";
///
/// 服务描述,显示在服务列表UI上的名称
///
public override string Description => "PipeLineLems服务";
///
/// 启用授权
///
public override bool AuthRequired => true;
///
/// Initializes a new instance of the class.
///
/// The logger.
/// The variable data cache.
public PipeLineLemsProjectService(
VariableService variableService,
IServiceProvider serviceProvider, ILogger logger, IVariableDataCache variableDataCache)
{
_serviceProvider = serviceProvider;
_logger = logger;
_variableDataCache = variableDataCache;
_variableService = variableService;
}
///
/// 开启服务
///
/// 具有工程上下文的实例
public override async Task StartAsync(IServiceProvider serviceProvider)
{
if (State == ProjectServiceState.Started)
{
return;
}
// 监听变量
_monitorVariableNames = new Dictionary
{
{ "打码进站信号", "打码进站信号(描述)" },
{ "切割进站信号", "切割进站信号(描述)" },
};
// 创建通道监听
_channelListener?.Token?.Dispose();
_channelListener = new FlowVariableChannelListener(_logger, _variableDataCache);
_channelListener.CreateChannel(Key, waitListener: false, timeout: TimeSpan.FromSeconds(30), variableFilter: _monitorVariableNames.Keys.ToHashSet());
_channelListener.TagChanged += OnTagValueChanged;
await base.StartAsync(serviceProvider);
}
///
/// 停止服务
///
/// 具有工程上下文的实例
public override async Task StopAsync(IServiceProvider serviceProvider)
{
if (_channelListener != null)
{
// 释放监听
_channelListener.TagChanged -= OnTagValueChanged;
_channelListener.Token.Dispose();
_channelListener = null;
}
// 使用后台作业异步处理
//await _serviceProvider.GetRequiredService().EnqueueAsync(new PipeLineLemsArgs
//{
// Subject = "PipeLineLems_Subject",
// Body = "PipeLineLems_Body",
//});
await base.StopAsync(serviceProvider);
}
///
/// Called when [tag value changed].
///
/// The sender.
/// The instance containing the event data.
private async void OnTagValueChanged(object sender, TagChangedEventArgs e)
{
var changeds = e.Changeds.Where(x => _monitorVariableNames != null && _monitorVariableNames.ContainsKey(x.Name));
if (!changeds.Any())
{
return;
}
foreach (var changed in changeds)
{
var oldValue = changed.Old?.Value;
var newValue = changed.New?.Value;
var traceId = e.TraceId;
_logger.LogInformation($"{changed.Name} 变量值发生变化,旧值{oldValue}=新值{newValue},TraceId={traceId}");
if (changed.Name == "打码进站信号" && changed.New?.Value.SafeString().ToBool() == true)
{
// TODO: 处理变量值变化
// Tips:https://cms-docs.shengyc.com/cms/api/%E5%90%8E%E7%AB%AF#3-%E5%8F%98%E9%87%8F%E6%A8%A1%E5%9D%97
/* 说明:通过订阅 IVariableDataCache.TagChanged 事件,您可以实时监控变量的变化。此事件会传递所有变量至事件处理函数,因此,业务层需在函数中筛选关注的变量。
注意事项:
(1)性能影响: 发布事件时,事件的发送者将阻塞流程。因此,强烈建议避免在事件处理函数中执行 I/ O 操作、HTTP 接口访问或其他耗时操作,以防止对系统性能产生严重影响,导致整个系统响应延迟。
(2)高频率触发: 由于事件订阅了全量变量,触发频率可能非常高。
(3)异步处理: 鉴于事件触发频率很高,建议业务层在筛选关注变量后,使用 Task 启动新线程处理业务逻辑,以避免阻塞核心的变量监听功能,实现业务层与平台基座的解耦。
(4)并发管理: 如果业务层并发量大,必须优化代码设计和实施,以减少在高并发情况下的系统资源消耗,防止系统性能问题。
(5)代码安全: 安装并使用 CMS.CodeAnalysis 分析器来分析 IVariableDataCache.TagChanged 的使用情况。该工具能在使用不当时提供编译错误,帮助您提高代码质量。*/
_ = Task.Run(async () =>
{
await HanlderForPringBarCodeAsync();
// 例1:同步处理
//await ProcessAsync();
// 例2:调用外部API
//await ExecuteExternalApiAsync();
});
}
if (changed.Name == "切割进站信号" && changed.New?.Value.SafeString().ToBool() == true)
{
// TODO: 处理变量值变化
// Tips:https://cms-docs.shengyc.com/cms/api/%E5%90%8E%E7%AB%AF#3-%E5%8F%98%E9%87%8F%E6%A8%A1%E5%9D%97
/* 说明:通过订阅 IVariableDataCache.TagChanged 事件,您可以实时监控变量的变化。此事件会传递所有变量至事件处理函数,因此,业务层需在函数中筛选关注的变量。
注意事项:
(1)性能影响: 发布事件时,事件的发送者将阻塞流程。因此,强烈建议避免在事件处理函数中执行 I/ O 操作、HTTP 接口访问或其他耗时操作,以防止对系统性能产生严重影响,导致整个系统响应延迟。
(2)高频率触发: 由于事件订阅了全量变量,触发频率可能非常高。
(3)异步处理: 鉴于事件触发频率很高,建议业务层在筛选关注变量后,使用 Task 启动新线程处理业务逻辑,以避免阻塞核心的变量监听功能,实现业务层与平台基座的解耦。
(4)并发管理: 如果业务层并发量大,必须优化代码设计和实施,以减少在高并发情况下的系统资源消耗,防止系统性能问题。
(5)代码安全: 安装并使用 CMS.CodeAnalysis 分析器来分析 IVariableDataCache.TagChanged 的使用情况。该工具能在使用不当时提供编译错误,帮助您提高代码质量。*/
_ = Task.Run(async () =>
{
await HanlderForCutAsync();
// 例1:同步处理
//await ProcessAsync();
// 例2:调用外部API
//await ExecuteExternalApiAsync();
});
}
}
}
///
/// Processes the asynchronous.
///
private async Task ProcessAsync()
{
using var scope = _serviceProvider.CreateScope();
var unitOfWorkManager = scope.ServiceProvider.GetRequiredService();
using var uow = unitOfWorkManager.Begin(requiresNew: true);
var mytestentitynameRepository = scope.ServiceProvider.GetRequiredService();
var count = await mytestentitynameRepository.GetCountAsync();
// 如果有更新数据库操作,需提交保存
// await uow.SaveChangesAsync();
_logger.LogInformation($"ProcessAsync,Count={count}");
}
///
/// Executes the external API.
///
private async Task ExecuteExternalApiAsync()
{
try
{
await _serviceProvider.GetRequiredService().CreateAsync(new MyTestEntityNameCreateDto
{
Name = "MyTestEntityName_Name",
Code = "MyTestEntityName_Code",
});
}
catch (Exception e)
{
_logger.LogException(e);
}
}
///
/// 打码
///
///
private async Task HanlderForPringBarCodeAsync()
{
var workPlanAppService = _serviceProvider.GetRequiredService();
var workPlanRepository = _serviceProvider.GetRequiredService();
var callMaterialOrderAppService = _serviceProvider.GetRequiredService();
using var scope = _serviceProvider.CreateScope();
var unitOfWorkManager = scope.ServiceProvider.GetRequiredService();
using var uow = unitOfWorkManager.Begin(requiresNew: true);
var plcTaskNo = await _variableService.ReadValueAsync("打码进站PLC任务号");
if (string.IsNullOrEmpty(plcTaskNo?.Content?.Value.SafeString().ToString()))
{
}
else
{
var myTaskNo = plcTaskNo.Content.Value.SafeString().ToString();
CallMaterialOrder callMaterialOrder = null;
try
{
//根据wms任务号寻找 叫料工单
callMaterialOrder = await callMaterialOrderAppService.FindByWmsTaskNoAsync(myTaskNo);
if (callMaterialOrder == null) return;//结束
//根据原料标识寻找 作业计划
var workPlanList = await workPlanAppService.FindByDataIdentifierAsync(callMaterialOrder.DataIdentifier);
if (workPlanList?.Count == 0) return;//结束
//更新为生产中
foreach (var item in workPlanList)
{
item.WorkPlanStatus = Domain.Shared.Enums.WorkPlanStatusEnum.生产中;
}
await workPlanRepository.UpdateManyAsync(workPlanList);
//得到码值
var code1 = "";
var code2 = "";
var code3 = "";
var new_workPlanList = workPlanList.Where(x => x.ProcessRouteNumber == "切割").ToList();
for (int i = 0; i < new_workPlanList.Count; i++)
{
if (i == 0)
{
code1 = new_workPlanList[i].MarkingContent;
}
if (i == 1)
{
code2 = new_workPlanList[i].MarkingContent;
}
if (i == 2)
{
code3 = new_workPlanList[i].MarkingContent;
}
}
Dictionary keyValuePairs = new Dictionary
{
{ "打码工件1",code1},
{ "打码工件2", code2},
{ "打码工件3", code3 },
{ "打码原料管型号", new_workPlanList.First().MaterialMode },
{ "打码原料标识", new_workPlanList.First().DataIdentifier }
};
_variableService.WriteValueAsync(keyValuePairs);
//TODO:模拟采集参数
keyValuePairs = new Dictionary
{
{ "打码速度", 100},
{ "打码质量", 2},
};
var ret2 = _variableService.WriteValueAsync(keyValuePairs);
//TODO:暂时生成产品ID
var productID = DateTime.Now.ToString("yyyyMMddHHmmssfff");
//var productID = new_workPlanList.Last().PipeSpecCode;
keyValuePairs = new Dictionary
{
{ "打码_ProductID", productID},
};
var ret = _variableService.WriteValueAsync(keyValuePairs);
uow.CompleteAsync();
}
catch (Exception)
{
uow.RollbackAsync();
throw;
}
}
}
///
/// 切割
///
///
private async Task HanlderForCutAsync()
{
var plcTaskNo = await _variableService.ReadValueAsync("切割进站PLC任务号");
if (string.IsNullOrEmpty(plcTaskNo?.Content?.Value.SafeString().ToString()))
{
}
else
{
var myTaskNo = plcTaskNo.Content.Value.SafeString().ToString();
//TODO:暂时先写入 内部变量
Dictionary keyValuePairs = new Dictionary
{
{ "切割位置1", "555555" },
{ "切割位置2", "66666" },
{ "切割位置3", "77777" }
};
_variableService.WriteValueAsync(keyValuePairs);
//TODO:模拟采集参数
keyValuePairs = new Dictionary
{
{ "切割速度", 99},
{ "切割质量", 1},
};
var ret2 = _variableService.WriteValueAsync(keyValuePairs);
//TODO:暂时生成产品ID
//获取上一个工序的产品ID
//根据工序名获取工序对象
var _workSectionManager = _serviceProvider.GetRequiredService();
var lastWorkSection = "打码工序";
var workSection = await _workSectionManager.GetByNameAsync(lastWorkSection);
//获取工单数据(从末工序查询3个产品)
//读取scms_productions表,根据当前时间查询最近3条记录
var traceManager = _serviceProvider.GetRequiredService();
GetTracesRequest request = new GetTracesRequest()
{
WorkSectionId = workSection.Id,
};
TraceModel traceModel = null;
var list = await traceManager.GetTracesAsync(request);
if (list?.Count > 0)
{
//重新排序
list = list.OrderByDescending(x => x.FinishTime).ToList();
traceModel = list.First();
}
//var productID = Guid.NewGuid().ToString();
var productID = DateTime.Now.ToString("yyyyMMddHHmmssfff");
if (traceModel != null)
{
productID = traceModel.SerialNumber;
}
keyValuePairs = new Dictionary
{
{ "切割_ProductID", productID},
};
var ret = _variableService.WriteValueAsync(keyValuePairs);
}
}
}
}