办法总比困难多(不会或不想用WPF MVVM模式也可以搞工控自动化和上位机编程)...
正文
活到老就该学到老。但是难道我们不可以偷懒么,老技术指哪打哪,游刃有余,如果已经炉火纯青,那么解决问题又快又好它不香吗。本文拒绝讨论技术谁优秀谁该被鄙视。上图的流程是花2天时间搞好的,我不是得瑟有什么不得了的地方。我见很多人都在探讨MVVM,数据驱动业务多么的了不起,其实老技术Winform一样的能办到不信你问问DeepSeek让它给你一个示例代码。比如这样子的,点这里下载。如果只是这么简单的话,就没有写本文的必要了。

我们看看上图的场景,因为现场的实际方案没有最终确定,所以开发人员写了5个版本的代码来验证5种流程,但界面就1个,因为业务的数据是一样的。像这种情况我们这种解决方式是极好的。写代码的同事只需要初级程序员就可以担任。帮忙验证和测试是由高级程序员来担任。我贴一些源码来辅助说明一下是怎么实现的。
1.界面的基类:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
using MES.Core;
using EES.Common;
using EES.Controls.Win;
using EES.Common.Data;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
using System.Linq;
using WinControls;
using DataCool_MES.framework;
using DataCool_MES.config;
using DataCool_MES.utils;
using System.Drawing;
namespace DataCool_MES
{
public delegate void FeedInfoHandler(object sender, Exception ex, string message, MessageTipsLevel level);
/// <summary>
/// 业务窗体基类
/// </summary>
public partial class BaseModuleForm : DockBaseWorkForm
{
#region DllImport
[DllImport("kernel32")]
public static extern long WritePrivateProfileString(string section, string key, string val, string filepath);
[DllImport("kernel32")]
public static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retval, int size, string filePath);
[DllImport("imm32.dll")]
public static extern IntPtr ImmGetContext(IntPtr hwnd);
[DllImport("imm32.dll")]
public static extern bool ImmGetOpenStatus(IntPtr himc);
[DllImport("imm32.dll")]
public static extern bool ImmSetOpenStatus(IntPtr himc, bool b);
[DllImport("imm32.dll")]
public static extern bool ImmGetConversionStatus(IntPtr himc, ref int lpdw, ref int lpdw2);
[DllImport("imm32.dll")]
#endregion
public static extern int ImmSimulateHotKey(IntPtr hwnd, int lngHotkey);
private const int IME_CMODE_FULLSHAPE = 0x8;
private const int IME_CHOTKEY_SHAPE_TOGGLE = 0x11;
private SynchronizationContext sc;
protected string onceScanData = string.Empty;
private BarCodeHooK hook;
IModuleFlow coreFlowObj;
// 获取屏幕分辨率
private Rectangle primaryScreen;
private float scaleX; // 基准分辨率宽度
private float scaleY;
[Browsable(false)]
public IModuleFlow CoreFlowObj
{
get { return coreFlowObj; }
}
[Browsable(false)]
/// <summary>
/// 日志记录处理异常信息显示处理对象
/// 此处用于各种日志和状态信息的记录,并进行对应的信息的显示
/// </summary>
internal virtual AlertLog Log
{
get
{
return null;
}
}
protected event Action CursorBegin;
protected event Action CursorEnd;
/// <summary>
/// 实例化脚本对象
/// </summary>
/// <returns></returns>
protected virtual IModuleFlow CreateModuleFlow()
{
return null;
}
public BaseModuleForm()
{
DoubleBuffered = true;
ImeMode = ImeMode.Off;
AutoScaleMode = AutoScaleMode.None;
InitializeComponent();
#region Cursor
CursorBegin = () =>
{
Clear();
Cursor = Cursors.WaitCursor;
};
CursorEnd = () =>
{
Cursor = Cursors.Default;
};
#endregion
Load += BaseModuleForm_Load;
hook = new BarCodeHooK();
hook.BarCodeEvent += Hook_BarCodeEvent;
hook.Start();
FormClosed += BaseModuleForm_FormClosed;
}
private void BaseModuleForm_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
hook.Stop();
hook.BarCodeEvent -= Hook_BarCodeEvent;
hook = null;
}
catch (Exception ex)
{
FrameAppContext.GetInstance().AppLogger.Error(ex);
}
}
private void Hook_BarCodeEvent(BarCodeHooK.BarCodes barCode)
{
if (CoreFlowObj != null && FlowContext.Instance.WorkStatus == WorkStatus.Running && !string.IsNullOrEmpty(barCode.BarCode))
{
onceScanData = TrimSpecialChar(barCode.BarCode);
CoreFlowObj.OnExecScanReceiving(onceScanData);
}
}
private void BaseModuleForm_Load(object sender, EventArgs e)
{
sc = SynchronizationContext.Current;
if (!DesignMode)
{
Input = FlowContext.Instance;
FlowContext.Instance.FlowCustomEvent += OnRaiseFlowCustomEvent;
}
}
private void OnRaiseFlowCustomEvent(object sender, TEventArgs<EventClass> e)
{
sc.Post(new SendOrPostCallback(delegate (object obj)
{
OnFlowCustomEvent((EventClass)obj);
}), e.Data);
}
protected virtual void OnFlowCustomEvent(EventClass ec)
{
if (ec.EventType == FlowViewCommand.ClearCurText)
{
Clear();
}
}
/// <summary>
/// 获取当前窗体绑定的上下文对象
/// </summary>
/// <returns></returns>
public virtual BindingType GetBindingType()
{
Type type;
if (bindingSource.DataSource is Type)
{
type = (Type)bindingSource.DataSource;
}
else
{
if (bindingSource.DataSource != null)
{
type = bindingSource.DataSource.GetType();
}
else
{
type = null;
}
}
if (type == null)
{
throw new Exception("界面输入的数据源为空");
}
BindingType bindingType = new BindingType();
Type type2;
if (type.GUID == typeof(DataCollection<>).GUID)
{
type2 = type.GetGenericArguments()[0];
bindingType.IsArray = true;
}
else
{
if (type.GUID == typeof(List<>).GUID)
{
type2 = type.GetGenericArguments()[0];
bindingType.IsArray = true;
}
else
{
type2 = type;
bindingType.IsArray = false;
}
}
bindingType.Type = Factory.getRawType(type2);
return bindingType;
}
public virtual void Start()
{
if (coreFlowObj != null && FlowContext.Instance.WorkStatus == WorkStatus.Running)
return;
IModuleFlow core = null;
try
{
CursorBegin.Invoke();
if (CoreFlowObj != null)
throw new Exception("正在作业,不能启动!");
core = CreateModuleFlow();
if (core != null)
{
core.FeedInfo += OnFeedInfo;
core.ExecBeginWork();
coreFlowObj = core;
FlowContext.Instance.WorkStatus = WorkStatus.Running;
}
ImeMode = ImeMode.NoControl;
Log.SetLogFocus();
}
catch (Exception ex)
{
FlowContext.Instance.WorkStatus = WorkStatus.NoAction;
if (coreFlowObj != null)
{
coreFlowObj.FeedInfo -= OnFeedInfo;
coreFlowObj = null;
}
throw ex;
}
finally
{
if (FlowContext.Instance.WorkStatus == WorkStatus.Running)
{
onceScanData = string.Empty;
}
CursorEnd.Invoke();
}
}
public virtual void Stop()
{
if (CoreFlowObj != null)
{
try
{
var configObj = SerializerHelper.LoadFromXML<AppContentConfig>(FrameAppContext.GetInstance().RunTimeConfigPath + "\\AppContentConfig.Config");
if (configObj != null && !string.IsNullOrEmpty(configObj.EntryModuleName) && configObj.EntryModuleName.Equals("成品装箱作业"))
{
using (var dlgFrm = new FlowStopCheckForm())
{
var dlg = dlgFrm.ShowDialog();
if (dlg == DialogResult.OK && !FlowContext.Instance.IsEndPacking)
{
CoreFlowObj.ExecEndWork();
coreFlowObj = null;
FlowContext.Instance.WorkStatus = WorkStatus.Stopped;
}
if (dlg == DialogResult.OK && FlowContext.Instance.IsEndPacking)
{
CoreFlowObj.StopWorkCheck();
}
}
}
else
{
CoreFlowObj.ExecEndWork();
coreFlowObj = null;
FlowContext.Instance.WorkStatus = WorkStatus.Stopped;
}
}
catch (Exception ex)
{
Exception error = new Exception("流程执行异常停止," + ex.InnerException == null ? ex.Message : ex.InnerException.Message);
FrameAppContext.GetInstance().AppLogger.Error(error);
}
finally
{
Clear();
if (FlowContext.Instance.WorkStatus != WorkStatus.Running)
{
Log.Write("作业已经停止!", "", MessageTipsLevel.Warning);
FrameAppContext.GetInstance().AppLogger.Info("作业已经停止!");
FlowContext.Instance.ResetContext();
}
Utility.GCFullCollect();
}
}
}
public virtual void Pause()
{
coreFlowObj.ExecPauseWork();
coreFlowObj = null;
FlowContext.Instance.WorkStatus = WorkStatus.Pauseing;
Clear();
Log.Write("作业已经暂停!", "", MessageTipsLevel.Warning);
}
/// <summary>
/// 界面UI控件的绑定数据清理
/// 此处为虚函数,用于UI派生界面的具体绑定调用处理
/// </summary>
protected virtual void Clear()
{
}
/// <summary>
/// 异常信息消息处理队列的回调函数
/// 主要用于各种消息或异常信息的分类处理
/// </summary>
/// <param name="sender"></param>
/// <param name="ex">异常消息结构体</param>
/// <param name="message">异常消息</param>
/// <param name="level">异常消息级别</param>
protected void OnFeedInfo(object sender, Exception ex, string message, MessageTipsLevel level)
{
if (Log == null)
{
throw new Exception("AlertLog对象没有绑定或实例化!");
}
if (ex != null)
{
Log.Write(string.Format("{0}:->", DateTime.Now.ToString("MM/dd HH:mm:ss")), ex.Message, MessageTipsLevel.Error, true);
}
else
{
Log.Write(string.Format("{0}:->", DateTime.Now.ToString("MM/dd HH:mm:ss")), message, level, true);
}
}
/// <summary>
/// 响应扫描枪输入
/// </summary>
/// <param name="msg"></param>
/// <param name="keyData"></param>
/// <returns></returns>
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
#region 快捷键
if (msg.Msg == 0x0100 && ContextMenuStrip != null)
{
foreach (ToolStripMenuItem item in ContextMenuStrip.Items)
{
if (keyData == item.ShortcutKeys)
{
item.PerformClick();
}
}
}
#endregion
if (FlowContext.Instance.WorkStatus != WorkStatus.Running)
return base.ProcessCmdKey(ref msg, keyData);
else
return true;
}
/// <summary>
/// 截取特殊字符
/// </summary>
/// <param name="inputString"></param>
/// <returns></returns>
protected virtual string TrimSpecialChar(string inputString)
{
return inputString.Replace("\u0010", "");
}
/// <summary>
/// 动态绑定DataGridView的列
/// </summary>
/// <param name="t"></param>
/// <param name="dgv"></param>
protected virtual void BingGridColumn(Type t, DataGridView dgv)
{
dgv.Columns.Clear();
var props = t.GetProperties().Where(prop => prop.GetCustomAttributes(typeof(DisplayNameAttribute), false).Any());
int col_width = dgv.Width / props.Count() - 8;
foreach (var prop in props)
{
DisplayNameAttribute attrib = (DisplayNameAttribute)prop.GetCustomAttributes(typeof(DisplayNameAttribute), false).FirstOrDefault();
if (prop.GetCustomAttributes(typeof(ProgressBarCellAttribute), false).Any())
{
DataGridViewProgressBarColumn dc = new DataGridViewProgressBarColumn();
dc.Name = "col" + prop.Name;
dc.HeaderText = attrib.DisplayName;
dc.DataPropertyName = prop.Name;
dc.Width = col_width;
dgv.Columns.Add(dc);
}
else
{
DataGridViewTextBoxColumn dc = new DataGridViewTextBoxColumn();
dc.Name = "col" + prop.Name;
dc.HeaderText = attrib.DisplayName;
dc.DataPropertyName = prop.Name;
dc.Width = col_width;
dgv.Columns.Add(dc);
}
}
}
/// <summary>
/// 窗体激活时设置输入法是半角
/// </summary>
/// <param name="e"></param>
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
IntPtr HIme = ImmGetContext(this.Handle);
if (ImmGetOpenStatus(HIme)) //如果输入法处于打开状态
{
int iMode = 0;
int iSentence = 0;
bool bSuccess = ImmGetConversionStatus(HIme, ref iMode, ref iSentence); //检索输入法资讯
if (bSuccess)
{
if ((iMode & IME_CMODE_FULLSHAPE) > 0) //如果是全形
ImmSimulateHotKey(this.Handle, IME_CHOTKEY_SHAPE_TOGGLE); //转换成半形
}
}
}
/// <summary>
/// 缩放控件
/// </summary>
/// <param name="control"></param>
protected void ScaleControl(Control control)
{
control.Left = (int)(control.Left * scaleX);
control.Top = (int)(control.Top * scaleY);
control.Width = (int)(control.Width * scaleX);
control.Height = (int)(control.Height * scaleY);
foreach (Control child in control.Controls)
{
ScaleControl(child);
}
}
}
}
2、代码的基类
using EES.Common;
using System;
using MES.Core;
namespace DataCool_MES
{
/// <summary>
/// 脚本的基类
/// </summary>
public abstract class ModuleBaseFlow : IModuleFlow
{
/// <summary>
/// 传递消息的事件
/// </summary>
public event FeedInfoHandler FeedInfo;
protected FlowContext flowContext = FlowContext.Instance;
//是否是调试模式
protected bool debugMode = false;
//是否显示过程明细信息
protected bool showDetail = false;
/// <summary>
/// 采集到数据后发生
/// </summary>
/// <param name="data"></param>
public virtual void OnExecScanReceiving(string data)
{
FrameAppContext.GetInstance().AppLogger.Info(data);
}
/// <summary>
/// 触发消息传递
/// </summary>
/// <param name="sender"></param>
/// <param name="ex"></param>
/// <param name="message"></param>
/// <param name="level"></param>
protected virtual void OnFeedInfo(object sender, Exception ex, string message, MessageTipsLevel level)
{
FeedInfo?.Invoke(sender, ex, message, level);
if (ex != null)
OnException(ex);
}
/// <summary>
/// 手动处理脚本触发的异常
/// </summary>
/// <param name="ex"></param>
protected virtual void OnException(Exception ex)
{
}
/// <summary>
/// 触发抛出异常
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
/// <param name="args"></param>
protected virtual void RaiseException(object sender, string message, params object[] args)
{
OnFeedInfo(sender, new Exception(string.Format(message, args)), message, MessageTipsLevel.Error);
if (!message.Contains("由于目标计算机积极拒绝"))
throw new Exception(message);
}
/// <summary>
/// 触发消息提示
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
protected void RaiseInfo(object sender, string message)
{
OnFeedInfo(sender, null, message, MessageTipsLevel.Information);
}
/// <summary>
/// 触发警告信息
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
protected void RaiseWarning(object sender, string message)
{
OnFeedInfo(sender, null, message, MessageTipsLevel.Warning);
}
/// <summary>
/// 脚本触发界面自定义事件响应
/// </summary>
/// <param name="eventType"></param>
/// <param name="portKey"></param>
/// <param name="portName"></param>
/// <param name="data"></param>
public virtual void RaiseCustomEvent(string eventType, string portKey, string portName, object data)
{
try
{
FlowContext.Instance.RaiseCustomEvent(this, eventType, portKey, portName, data);
}
catch (Exception ex)
{
RaiseException(this, ex.Message);
}
}
/// <summary>
/// 执行开始作业
/// </summary>
public virtual void ExecBeginWork()
{
try
{
BeforeWorkInit();
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 执行结束作业
/// </summary>
public virtual void ExecEndWork()
{
}
/// <summary>
/// 执行暂停作业
/// </summary>
public virtual void ExecPauseWork()
{
try
{
PauseWorkCache();
}
catch (Exception ex)
{
throw ex;
}
finally
{
FlowContext.Instance.ResetContext();
}
}
/// <summary>
/// 开始作业前初始化
/// </summary>
protected virtual void BeforeWorkInit()
{
RaiseInfo(this,"作业条件检查中...");
}
/// <summary>
/// 结束作业时执行检查
/// </summary>
public virtual void StopWorkCheck()
{
}
/// <summary>
/// 暂停作业时缓存业务临时数据
/// </summary>
protected virtual void PauseWorkCache()
{
}
}
}
3.业务窗体
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using MES.Core;
using MES.Services;
using DataCool_MES.modules.dto;
using MES.Services.ModuleFlowServices;
using System.Net.Sockets;
using System.IO;
using WinControls;
namespace DataCool_MES.modules
{
[FullScreenStyle(true)]
[View("批量扫描tray盘混料检测", EntryType.入口, "一次产品检验", "包装作业")]
public partial class BatchPackForm : BaseModuleForm
{
[Import]
protected IModuleFlowFormService ModuleFlowFormService { get; set; }
//已扫记录
private List<BatchScanLogDto> LogData = new List<BatchScanLogDto>();
private BatchScanLogDto currentBarcodeData = null;
private WorkStationConfig config;
private TcpClient cameraClient;
private TcpClient transmitDeviceClient;
private byte[] buffer = new byte[2048];
public BatchPackForm()
{
DoubleBuffered = true;
CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
config = SerializerHelper.LoadFromXML<WorkStationConfig>(AppDomain.CurrentDomain.BaseDirectory + "Config\\" + "WorkStationConfig.xml");
if (config != null)
{
FlowContext.Instance.TraySpecifications = config.TraySpecifications;
}
Load += BatchPackForm_Load;
FormClosed += BatchPackForm_FormClosed;
}
private void BatchPackForm_FormClosed(object sender, FormClosedEventArgs e)
{
if (transmitDeviceClient != null)
{
try
{
transmitDeviceClient.Client?.Disconnect(false);
transmitDeviceClient.Client?.Close();
transmitDeviceClient?.Close();
transmitDeviceClient = null;
}
catch
{
}
}
Program.Quit();
}
AlertLog log;
internal override AlertLog Log
{
get
{
if (log == null)
{
Interlocked.CompareExchange(ref log, new AlertLog(richTextBox1), null);
}
return log;
}
}
private void BatchPackForm_Load(object sender, EventArgs e)
{
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this, new FlowWorkService());
if (!System.Diagnostics.Debugger.IsAttached)
{
btnStart.DataBindings.Add(new Binding("Enabled", FlowContext.Instance, "IsStoped", false, DataSourceUpdateMode.OnPropertyChanged));
btnStop.DataBindings.Add(new Binding("Enabled", FlowContext.Instance, "IsRunning", false, DataSourceUpdateMode.OnPropertyChanged));
}
if (config != null && !string.IsNullOrEmpty(config.TransmissionDeviceIP))
{
try
{
transmitDeviceClient = new TcpClient();
transmitDeviceClient.BeginConnect(config.TransmissionDeviceIP, config.TransmissionDevicePort, new AsyncCallback(ConnectCallback2), transmitDeviceClient);
}
catch (Exception ex)
{
FrameAppContext.GetInstance().AppLogger.Error(ex);
}
}
}
protected override IModuleFlow CreateModuleFlow()
{
return new BatchScanWorkFlow(ModuleFlowFormService);
}
protected override void Clear()
{
richTextBox1.Text = "";
}
private void btnStart_Click(object sender, EventArgs e)
{
try
{
Start();
if (FlowContext.Instance.WorkStatus == WorkStatus.Running)
{
cameraClient = new TcpClient();
cameraClient.BeginConnect(config.CameraIP, config.CameraPort, new AsyncCallback(ConnectCallback), cameraClient);
}
if (FlowContext.Instance.WorkStatus == WorkStatus.Running)
{
try
{
if (transmitDeviceClient != null)
{
var sm = transmitDeviceClient.GetStream();
SendCommandToDevice(sm, "OK");
}
else
{
Log.Write("", "没有和传动设备建立连接!!!", MessageTipsLevel.Error);
}
}
catch (Exception ex)
{
Log.Write("", "和传动设备通信失败!!!" + Environment.NewLine + ex.Message, MessageTipsLevel.Error);
}
}
}
catch (Exception ex)
{
FrameAppContext.GetInstance().AppLogger.Error($"{ex.Message}");
}
}
void ConnectCallback(IAsyncResult ar)
{
TcpClient client = (TcpClient)ar.AsyncState;
try
{
client.EndConnect(ar);
NetworkStream stream = client.GetStream();
if (stream != null)
{
buffer = new byte[2048];
stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback), stream);
}
}
catch (Exception ex)
{
log.Write("", ex.Message, MessageTipsLevel.Error);
}
}
void ReadCallback(IAsyncResult ar)
{
NetworkStream stream = (NetworkStream)ar.AsyncState;
if (FlowContext.Instance.WorkStatus != WorkStatus.Running)
return;
int bytesRead = stream.EndRead(ar);
string responseData = Encoding.ASCII.GetString(buffer, 0, bytesRead);
log.Write("", $"Received: {Environment.NewLine}{responseData}", MessageTipsLevel.Information);
FrameAppContext.GetInstance().AppLogger.Info(Environment.NewLine + responseData);
if (!string.IsNullOrEmpty(responseData) && responseData.IndexOf("+") != -1)
{
ScanLog(responseData);
}
buffer = new byte[2048];
stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback), stream);
}
private void BtnStop_Click(object sender, EventArgs e)
{
try
{
Stop();
var config = SerializerHelper.LoadFromXML<WorkStationConfig>(AppDomain.CurrentDomain.BaseDirectory + "Config\\" + "WorkStationConfig.xml");
FlowContext.Instance.TraySpecifications = config.TraySpecifications;
if (config != null && !string.IsNullOrEmpty(config.TraySpecifications) &&
config.TraySpecifications.IndexOf(",") == -1)
{
string[] cellData = config.TraySpecifications.Split('*');
if (cellData.Length > 0)
{
FlowContext.Instance.TrayRowCount = Convert.ToInt32(cellData[1]);
FlowContext.Instance.TrayCellCount = Convert.ToInt32(cellData[0]);
}
}
if (cameraClient != null && FlowContext.Instance.WorkStatus != WorkStatus.Running)
{
cameraClient.Client?.Disconnect(false);
cameraClient.Client?.Close();
cameraClient?.Close();
cameraClient = null;
}
SendCommandToDevice(transmitDeviceClient.GetStream(), "NG");
}
catch (Exception ex)
{
FrameAppContext.GetInstance().AppLogger.Error($"{ex.Message}");
}
}
protected override void OnFlowCustomEvent(EventClass ec)
{
if (ec.EventType.Equals("LoadData"))
{
DataAction(LoadData);
}
if (ec.EventType.Equals("ExportResultData"))
{
DataAction(ExportLogToExcelFile);
}
}
/// <summary>
/// 从临时xml文件读取扫描历史记录
/// </summary>
private void SaveData()
{
if (!System.IO.Directory.Exists(AppDomain.CurrentDomain.BaseDirectory + "LocalData"))
{
System.IO.Directory.CreateDirectory(AppDomain.CurrentDomain.BaseDirectory + "LocalData");
}
if (!System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "LocalData\\" + DateTime.Now.ToString("yyyyMMdd") + ".xml"))
{
SerializerHelper.SerializerToXML(AppDomain.CurrentDomain.BaseDirectory + "LocalData\\" + DateTime.Now.ToString("yyyyMMdd") + ".xml", LogData);
}
else
{
System.IO.File.Delete(AppDomain.CurrentDomain.BaseDirectory + "LocalData\\" + DateTime.Now.ToString("yyyyMMdd") + ".xml");
SerializerHelper.SerializerToXML(AppDomain.CurrentDomain.BaseDirectory + "LocalData\\" + DateTime.Now.ToString("yyyyMMdd") + ".xml", LogData);
}
FlowContext.Instance.L1Count = LogData.Count;
if (LogData.Count > 0)
bindingSourceGrid.DataSource = LogData.OrderByDescending(t => t.ScanDatetime).ToList();
}
/// <summary>
/// 加载已有扫描记录
/// </summary>
private void LoadData()
{
if (!System.IO.Directory.Exists(AppDomain.CurrentDomain.BaseDirectory + "LocalData"))
{
System.IO.Directory.CreateDirectory(AppDomain.CurrentDomain.BaseDirectory + "LocalData");
}
if (System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "LocalData\\" + DateTime.Now.ToString("yyyyMMdd") + ".xml"))
{
LogData = SerializerHelper.LoadFromXML<List<BatchScanLogDto>>(AppDomain.CurrentDomain.BaseDirectory + "LocalData\\" + DateTime.Now.ToString("yyyyMMdd") + ".xml");
FlowContext.Instance.L1Count = LogData.Count;
if (LogData.Count > 0)
bindingSourceGrid.DataSource = LogData.OrderByDescending(t => t.ScanDatetime).ToList();
}
}
/// <summary>
/// 扫描到一组条码
/// </summary>
/// <param name="barcodeData"></param>
private void ScanLog(string barcodeData)
{
if (LogData.Any(t => t.BarcodeData == barcodeData))
{
log.Write(DateTime.Now + "->:", "重复扫描!", MessageTipsLevel.Error);
}
else
{
#region 传递收到的数据
var entity = new BatchScanLogDto();
entity.BarcodeData = barcodeData;
entity.MaterialCode = FlowContext.Instance.MaterielCode;
entity.ScanDatetime = DateTime.Now.ToString();
entity.TraySpecifications = FlowContext.Instance.TraySpecifications;
entity.ResourceText = barcodeData;
currentBarcodeData = entity;
#endregion
//校验数据
ThreeColorled1_Click(threeColorled1, new EventArgs());
}
}
private void ThreeColorled1_Click(object sender, EventArgs e)
{
//必须是符合业务场景的扫描数据
if (FlowContext.Instance.WorkStatus != WorkStatus.Running ||
currentBarcodeData == null ||
currentBarcodeData.BarcodeData.IndexOf("+") == -1)
return;
#region 展示解析结果
BarcodeResultViewForm barcodeResultViewForm = new BarcodeResultViewForm(currentBarcodeData);
var diagResult = barcodeResultViewForm.ShowDialog();
if (diagResult == DialogResult.OK &&
!LogData.Any(r => r.ResourceText == currentBarcodeData.ResourceText))
{
threeColorled1.StatusCode = "1";
log.Write("", "校验通过!", MessageTipsLevel.Information);
LogData.Add(barcodeResultViewForm.BarcodeData);
DataAction(SaveData);
FlowContext.Instance.L1Count = LogData.Count;
SendCommandToDevice(transmitDeviceClient.GetStream(), "OK");
}
else if (diagResult != DialogResult.OK &&
!LogData.Any(r => r.ResourceText == currentBarcodeData.ResourceText))
{
threeColorled1.StatusCode = "2";
var sm = transmitDeviceClient.GetStream();
SendCommandToDevice(sm, "NG");
log.Write("", $"{Environment.NewLine}校验结果是有混料,不予通行!!!", MessageTipsLevel.Warning);
}
#endregion
}
/// <summary>
/// 和传动设备建立Socket连接回调
/// </summary>
/// <param name="ar"></param>
void ConnectCallback2(IAsyncResult ar)
{
TcpClient client = (TcpClient)ar.AsyncState;
try
{
client.EndConnect(ar);
Log.Write("", "传动设备已准备就绪...", MessageTipsLevel.Information);
}
catch (Exception ex)
{
Log.Write("", "传动设备连接失败!"+ex.Message, MessageTipsLevel.Error);
FrameAppContext.GetInstance().AppLogger.Error(ex);
}
}
/// <summary>
/// 给NetworkStream发送指令
/// </summary>
/// <param name="stream"></param>
/// <param name="cmd"></param>
private void SendCommandToDevice(NetworkStream stream, string cmd)
{
string command = cmd;
byte[] commandBuffer = ASCIIEncoding.ASCII.GetBytes(command);
stream.WriteAsync(commandBuffer, 0, commandBuffer.Length);
}
/// <summary>
/// 导出Excel文件(扫码原始记录)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnExport_Click(object sender, EventArgs e)
{
if (LogData.Count == 0)
{
MessageTip.ShowError("还没有通过检验的记录不能导出!");
}
else
{
ExportExcel exportExcel = new ExportExcel();
string fileName = $"{AppDomain.CurrentDomain.BaseDirectory + "LocalData"}\\{DateTime.Now.Date:yyyyMMdd}{FlowContext.Instance.MaterielCode}.xls";
exportExcel.GridToExcel(fileName, this.dataGridView1);
if (File.Exists(fileName))
{
MessageTip.ShowOk($"Excel文件导出成功!");
}
}
}
/// <summary>
/// 导出Excel文件(MES要求的格式)
/// </summary>
private void ExportLogToExcelFile()
{
if (LogData.Count == 0)
return;
#region 临时表
var dt = new DataTable();
dt.Columns.Add(new DataColumn("条码", typeof(string)));
foreach (var logItem in LogData)
{
string[] barcodeList = logItem.BarcodeData.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var barcode in barcodeList)
{
DataRow dr = dt.NewRow();
dr[0] = barcode;
dt.Rows.Add(dr);
}
}
#endregion
try
{
ExportExcel exportExcel = new ExportExcel();
string fileName = $"{AppDomain.CurrentDomain.BaseDirectory + "LocalData"}\\{DateTime.Now.Date:yyyyMMdd}{FlowContext.Instance.MaterielCode}(仅条码).xls";
if (File.Exists(fileName))
File.Delete(fileName);
exportExcel.DataTableToExcel(fileName, dt);
if (File.Exists(fileName))
{
MessageTip.ShowOk("生产过程的全部条码已保存!");
string logMessage = $"本次生产共计扫描条码{dt.Rows.Count}个,通过tray盘{LogData.Count}盘.";
log.Write("", logMessage, MessageTipsLevel.Information);
FrameAppContext.GetInstance().AppLogger.Info(logMessage);
}
}
catch (Exception ex)
{
log.Write("", ex.Message, MessageTipsLevel.Error);
}
finally
{
dt.Dispose();
}
}
private void button1_Click(object sender, EventArgs e)
{
Program.Quit();
}
private void richTextBox1_DoubleClick(object sender, EventArgs e)
{
Clear();
}
}
}
4.流程代码
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using MES.Core;
using MES.Services;
namespace DataCool_MES.modules
{
/// <summary>
/// 批量扫描作业流程
/// </summary>
public class BatchScanWorkFlow : ModuleBaseFlow
{
private TcpClient cameraClient;
protected IModuleFlowFormService ModuleFlowFormService { get; set; }
/// <summary>
/// 析构函数
/// </summary>
/// <param name="svr"></param>
public BatchScanWorkFlow(IModuleFlowFormService svr)
{
ModuleFlowFormService = svr;
}
/// <summary>
/// 和阅读器建立Socket连接回调
/// </summary>
/// <param name="ar"></param>
void ConnectCallback(IAsyncResult ar)
{
try
{
TcpClient client = (TcpClient)ar.AsyncState;
client?.EndConnect(ar);
NetworkStream stream = client.GetStream();
if (stream != null)
{
RaiseInfo(this, "读码器已准备就绪...");
if (client != null)
{
stream.Close();
cameraClient?.Close();
}
}
}
catch (Exception ex)
{
RaiseException(this, "读码器连接失败!!!错误码:"+ex.Message);
}
}
/// <summary>
/// 给NetworkStream发送指令
/// </summary>
/// <param name="stream"></param>
/// <param name="cmd"></param>
private void SendCommandToDevice(NetworkStream stream, string cmd)
{
string command = cmd;
byte[] commandBuffer = ASCIIEncoding.ASCII.GetBytes(command);
stream.WriteAsync(commandBuffer, 0, commandBuffer.Length);
}
/// <summary>
/// 和传动设备建立Socket连接回调
/// </summary>
/// <param name="ar"></param>
void ConnectCallback2(IAsyncResult ar)
{
try
{
TcpClient client = (TcpClient)ar.AsyncState;
client?.EndConnect(ar);
NetworkStream stream = client.GetStream();
if (stream != null)
{
//发消息让皮带开始转动
SendCommandToDevice(stream, "OK");
}
}
catch (Exception ex)
{
RaiseException(this, "传动设备连接失败!!!错误码:" + ex.Message);
}
}
/// <summary>
/// 开始作业前准备工作
/// </summary>
protected override void BeforeWorkInit()
{
base.BeforeWorkInit();
if (string.IsNullOrEmpty(FlowContext.Instance.TraySpecifications))
{
RaiseException($"{DateTime.Now}->:", $"必须设定容器的规格!");
}
if (string.IsNullOrEmpty(FlowContext.Instance.MaterielCodeHeader+FlowContext.Instance.MaterielCodeEnder))
{
RaiseException($"{DateTime.Now}->:", $"必须输入本次校验的物料代码!");
}
flowContext.L1Count = 0;
RaiseCustomEvent("LoadData", "", "", "");
var config = SerializerHelper.LoadFromXML<WorkStationConfig>(AppDomain.CurrentDomain.BaseDirectory + "Config\\" + "WorkStationConfig.xml");
if (config != null && !string.IsNullOrEmpty(config.CameraIP))
{
try
{
cameraClient = new TcpClient();
cameraClient.BeginConnect(config.CameraIP, config.CameraPort, new AsyncCallback(ConnectCallback), cameraClient);
}
catch (Exception ex)
{
RaiseException(this,"",ex.Message);
}
}
else
{
RaiseException($"{DateTime.Now}->:", $"必须设定阅读器的IP地址!");
}
if (config != null && !string.IsNullOrEmpty(config.TraySpecifications) && config.TraySpecifications.IndexOf(",") == -1)
{
string[] cellData = config.TraySpecifications.Split('*');
if (cellData.Length > 0)
{
flowContext.TrayRowCount = Convert.ToInt32(cellData[1]);
flowContext.TrayCellCount = Convert.ToInt32(cellData[0]);
}
}
FlowContext.Instance.MaterielCode= FlowContext.Instance.MaterielCodeHeader+FlowContext.Instance.MaterielCodeEnder;
RaiseInfo(this, "系统准备就绪...");
}
/// <summary>
/// 扫描到一个条码
/// </summary>
/// <param name="data"></param>
public override void OnExecScanReceiving(string data)
{
RaiseCustomEvent("ScanLog", "", "", data);
}
/// <summary>
/// 结束本次作业
/// </summary>
public override void ExecEndWork()
{
base.ExecEndWork();
//导出扫描记录
RaiseCustomEvent("ExportResultData", "", "", "");
}
}
}
5.界面绑定代码
protected override IModuleFlow CreateModuleFlow()
{
return new BatchScanWorkFlow(ModuleFlowFormService);
}

界面和代码中间还有个上下文对象,也就是一个单例,貌似这样子:
using EES.Common;
using MES.Core;
using System;
using System.ComponentModel;
namespace DataCool_MES
{
/// <summary>
/// 工作流程上下文对象
/// </summary>
[Serializable]
public class FlowContext : SingletonProvider<FlowContext>, INotifyPropertyChanged
{
public System.Configuration.Configuration AppConfig { get; set; }
public event EventHandler<TEventArgs<EventClass>> FlowCustomEvent;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private WorkStatus workStatus;
/// <summary>
/// 工作流程运行状态
/// </summary>
public WorkStatus WorkStatus
{
get { return workStatus; }
set
{
workStatus = value;
OnPropertyChanged("WorkStatus");
switch (value)
{
case WorkStatus.Running:
IsRunning = true;
IsStoped = false;
WorkStatusName = "运行中...";
break;
case WorkStatus.Pauseing:
IsRunning = false;
IsStoped = true;
WorkStatusName = "暂停中...";
break;
case WorkStatus.Stopped:
IsRunning = false;
IsStoped = true;
WorkStatusName = "已经停止...";
break;
default:
IsRunning = false;
IsStoped = true;
WorkStatusName = "未启动";
break;
}
}
}
private string workStation;
/// <summary>
/// 工位
/// </summary>
public string WorkStation
{
get { return workStation; }
set
{
workStation = value;
OnPropertyChanged("WorkStation");
}
}
private string _operator;
/// <summary>
/// 工位操作员
/// </summary>
public string Operator
{
get { return _operator; }
set { _operator = value; OnPropertyChanged("Operator"); }
}
private string lineNo;
/// <summary>
/// 线号
/// </summary>
public string LineNo
{
get { return lineNo; }
set { lineNo = value; OnPropertyChanged("LineNo"); }
}
private string _L1Code;
/// <summary>
/// PCBA条码
/// </summary>
public string L1Code
{
get { return _L1Code; }
set { _L1Code = value; OnPropertyChanged("L1Code"); }
}
private string _L2Code;
/// <summary>
/// 过程条码
/// </summary>
public string L2Code
{
get { return _L2Code; }
set { _L2Code = value; OnPropertyChanged("L2Code"); }
}
private string workStatusName;
/// <summary>
/// 状态状态描述信息
/// </summary>
public string WorkStatusName
{
get { return workStatusName; }
set { workStatusName = value; OnPropertyChanged("WorkStatusName"); }
}
private int _L1Count;
/// <summary>
/// 第一道工序计数
/// </summary>
public int L1Count
{
get { return _L1Count; }
set
{
_L1Count = value;
OnPropertyChanged("L1Count");
}
}
private int _L1CountEx = 0;
/// <summary>
/// 制令计数,可清零
/// </summary>
public int L1CountEx
{
get { return _L1CountEx; }
set
{
_L1CountEx = value;
OnPropertyChanged("L1CountEx");
}
}
private int _L2Count;
/// <summary>
/// 第二道工序计数
/// </summary>
public int L2Count
{
get { return _L2Count; }
set { _L2Count = value; OnPropertyChanged("L2Count"); }
}
private string _TraySpecifications;
/// <summary>
///Tray Specifications
/// </summary>
public string TraySpecifications
{
get { return _TraySpecifications; }
set
{
_TraySpecifications = value;
OnPropertyChanged("TraySpecifications");
}
}
public int TrayCellCount
{
get;set;
}
public int TrayRowCount
{
get; set;
}
private string orderNo;
/// <summary>
/// 状态状态描述信息
/// </summary>
public string OrderNo
{
get { return orderNo; }
set
{
orderNo = value;
OnPropertyChanged("OrderNo");
}
}
private string _MaterielCode;
/// <summary>
/// 物料编码
/// </summary>
public string MaterielCode
{
get { return _MaterielCode; }
set { _MaterielCode = value; OnPropertyChanged("MaterielCode"); }
}
private string _MaterielCodeEnder;
/// <summary>
/// 物料编码
/// </summary>
public string MaterielCodeEnder
{
get { return _MaterielCodeEnder; }
set { _MaterielCodeEnder = value; OnPropertyChanged("_MaterielCodeEnder"); }
}
private string _MaterielCodeHeader;
/// <summary>
/// 物料编码
/// </summary>
public string MaterielCodeHeader
{
get { return _MaterielCodeHeader; }
set { _MaterielCodeHeader = value; OnPropertyChanged("MaterielCodeHeader"); }
}
private int progress;
/// <summary>
/// 本次扫描完成时的进度
/// </summary>
public int Progress
{
get { return progress; }
set { progress = value; OnPropertyChanged("Progress"); }
}
private bool isStoped = true;
/// <summary>
/// WorkStatus是否已经停止
/// </summary>
public bool IsStoped
{
get { return isStoped; }
set { isStoped = value; OnPropertyChanged("IsStoped"); }
}
private bool isRunning = false;
/// <summary>
/// WorkStatus是否正在运行
/// </summary>
public bool IsRunning
{
get { return isRunning; }
set { isRunning = value; OnPropertyChanged("IsRunning"); }
}
private string _Parameter1;
/// <summary>
/// 公用参数1
/// </summary>
public string Parameter1
{
get { return _Parameter1; }
set { _Parameter1 = value; OnPropertyChanged("Parameter1"); }
}
private string _Parameter2;
/// <summary>
/// 公用参数2
/// </summary>
public string Parameter2
{
get { return _Parameter2; }
set { _Parameter2 = value; OnPropertyChanged("Parameter2"); }
}
private string _Parameter3;
/// <summary>
/// 公用参数3
/// </summary>
public string Parameter3
{
get { return _Parameter3; }
set { _Parameter3 = value; OnPropertyChanged("Parameter3"); }
}
private string _Parameter4;
/// <summary>
/// 公用参数4
/// </summary>
public string Parameter4
{
get { return _Parameter4; }
set { _Parameter4 = value; OnPropertyChanged("Parameter4"); }
}
private string _Parameter5;
/// <summary>
/// 公用参数5
/// </summary>
public string Parameter5
{
get { return _Parameter5; }
set { _Parameter5 = value; OnPropertyChanged("Parameter5"); }
}
private string _Parameter6;
/// <summary>
/// 公用参数6
/// </summary>
public string Parameter6
{
get { return _Parameter6; }
set { _Parameter6 = value; OnPropertyChanged("Parameter6"); }
}
private string _Parameter7;
/// <summary>
/// 公用参数7
/// </summary>
public string Parameter7
{
get { return _Parameter7; }
set { _Parameter7 = value; OnPropertyChanged("Parameter7"); }
}
private string _Parameter8;
/// <summary>
/// 公用参数8
/// </summary>
public string Parameter8
{
get { return _Parameter8; }
set { _Parameter8 = value; OnPropertyChanged("Parameter8"); }
}
private string _Parameter9;
/// <summary>
/// 公用参数9
/// </summary>
public string Parameter9
{
get { return _Parameter9; }
set { _Parameter9 = value; OnPropertyChanged("Parameter9"); }
}
private int packagePCS;
/// <summary>
/// 二级包装,PCS数量
/// </summary>
public int PackagePCS_Count
{
get { return packagePCS; }
set { packagePCS = value; OnPropertyChanged("PackagePCS_Count"); }
}
private bool isEndPacking=false;
/// <summary>
/// 是否是在装尾箱
/// </summary>
public bool IsEndPacking
{
get { return isEndPacking; }
set { isEndPacking= value; OnPropertyChanged("IsEndPacking"); }
}
private int _WorkEndNumber;
public int WorkEndNumber
{
get { return _WorkEndNumber; }
set
{
_WorkEndNumber = value;
OnPropertyChanged("WorkEndNumber");
}
}
public void RaiseCustomEvent(object sender, string eventType, string portKey, string portName, object data)
{
RaiseCustomEvent(sender, eventType, portKey, portName, data, null);
}
public void RaiseCustomEvent(object sender, string eventType, string portKey, string portName, object data,
object extend)
{
if (FlowCustomEvent != null)
{
EventClass ec = new EventClass(sender, eventType, portKey, portName, data, extend);
FlowCustomEvent(this, new TEventArgs<EventClass>(ec));
}
}
/// <summary>
/// 重置上下文
/// </summary>
public void ResetContext()
{
OrderNo = string.Empty;
MaterielCode = string.Empty;
Progress = 0;
L1Code = string.Empty;
L1Count = 0;
L2Code = "";
L2Count = 0;
L1CountEx = 0;
TraySpecifications = "";
Parameter1 = "";
Parameter2 = "";
Parameter3 = "";
Parameter4 = "";
Parameter5 = "";
Parameter6 = "";
Parameter7 = "";
Parameter8 = "";
Parameter9 = "";
IsEndPacking = false;
}
}
}
好了,我的言传就是这样。我的表达能力就只能到这样了。