领胜LDS 键盘AOI检测项目
1. gocator添加byte2/byte4的配置,目前测试byte4不成功,暂时还是使用byte2配置。
2. 添加键名代码映射配置和NG键名代码上传功能
13个文件已修改
502 ■■■■ 已修改文件
src/Bro.Device.GTSCard/GTSCardDriver.cs 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.Gocator/GocatorConfig.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.Gocator/GocatorDriver.cs 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.HikCamera/Bro.Device.HikCamera.csproj 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/Bro.M071.Process.csproj 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/M071Config.cs 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/M071Models.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/M071Process.cs 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/M071Process_MES.cs 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/UI/KeyIndicator.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/UI/M071_MainForm.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Process/ProcessControl.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/ExcelTest/ExcelTest.csproj 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.GTSCard/GTSCardDriver.cs
@@ -646,7 +646,9 @@
                if (CurrentState != EnumHelper.DeviceState.DSOpen)
                {
                    LogAsync(DateTime.Now, "非正常状态异常", "轴" + optionPara.AxisIndex + "试图在非正常状态运动");
                    throw new ProcessException("轴" + optionPara.AxisIndex + "试图在非正常状态运动", null);
                    return false;
                    //throw new ProcessException("轴" + optionPara.AxisIndex + "试图在非正常状态运动", null);
                }
                LogAsync(DateTime.Now, "轴" + optionPara.AxisIndex + "开始运动", "目标坐标:" + optionPara.Destination);
@@ -725,8 +727,11 @@
                if (CurrentState != EnumHelper.DeviceState.DSOpen)
                {
                    LogAsync(DateTime.Now, "非正常状态异常", "轴" + optionPara.AxisIndex + "试图在非正常状态运动");
                    throw new ProcessException("轴" + optionPara.AxisIndex + "试图在非正常状态运动", null);
                    return false;
                    //throw new ProcessException("轴" + optionPara.AxisIndex + "试图在非正常状态运动", null);
                }
                LogAsync(DateTime.Now, "轴" + optionPara.AxisIndex + "开始运动", "目标坐标:" + optionPara.Destination);
                short ret = 0;
                bool isSuccessSetAxisParam = false;
@@ -783,7 +788,8 @@
                bool isStop = false;
                if (option == 1)
                {
                    StateChange(EnumHelper.DeviceState.DSExcept);
                    //StateChange(EnumHelper.DeviceState.DSExcept);
                    LogAsync(DateTime.Now, "急停停止", "");
                }
                var ret = GTSCardAPI.GT_Stop((short)IConfig.CardNum, 1 << (axisNum - 1), option);
                if (ret != (short)GTSRetCode.GRCRunOK)
src/Bro.Device.Gocator/GocatorConfig.cs
@@ -47,6 +47,16 @@
        [Description("true:异步/被动采图模式  false:同步/主动采图模式")]
        [DefaultValue(false)]
        public bool IsAsyncMode { get; set; } = false;
        [Category("数据位配置")]
        [Description("图像数据位数,2位/4位")]
        public GocatorDataByteNums ByteNums { get; set; } = GocatorDataByteNums.Byte2;
    }
    public enum GocatorDataByteNums
    {
        Byte2 = 2,
        Byte4 = 4,
    }
    [Device("Gocator", "Gocator激光扫描仪", EnumHelper.DeviceAttributeType.OperationConfig)]
src/Bro.Device.Gocator/GocatorDriver.cs
@@ -94,8 +94,9 @@
                            float zoomFactor = (float)((double)surfaceMsg.YResolution / (double)surfaceMsg.XResolution);
                            int zoomHeight = (int)(zoomFactor * height);
                            IntPtr zoomPtr = Marshal.AllocHGlobal(zoomHeight * (int)width * 2);
                            //IntPtr zoomPtr = Marshal.AllocHGlobal(zoomHeight * (int)width * 2);
                            //IntPtr zoomPtr = Marshal.AllocHGlobal(zoomHeight * (int)width * 4);
                            IntPtr zoomPtr = Marshal.AllocHGlobal(zoomHeight * (int)width * (int)IIConfig.ByteNums);
                            //没有插值,默认0
                            //Parallel.For(0, height, h =>
@@ -103,40 +104,45 @@
                            //      CopyMemory((IntPtr)((long)zoomPtr + width * 2 * Math.Floor(h * zoomFactor)), (IntPtr)((long)bufferPointer + width * 2 * h), width * 2);
                            //  });
                            //使用上一行原有数据
                            Parallel.For(0, zoomHeight, h =>
                            if (IIConfig.ByteNums == GocatorDataByteNums.Byte2)
                            {
                                int originHeightIndex = (int)Math.Floor((double)height * h / (double)zoomHeight);
                                //使用上一行原有数据
                                Parallel.For(0, zoomHeight, h =>
                                {
                                    int originHeightIndex = (int)Math.Floor((double)height * h / (double)zoomHeight);
                                CopyMemory((IntPtr)((long)zoomPtr + width * 2 * h), (IntPtr)((long)bufferPointer + width * 2 * originHeightIndex), width * 2);
                            });
                                    CopyMemory((IntPtr)((long)zoomPtr + width * 2 * h), (IntPtr)((long)bufferPointer + width * 2 * originHeightIndex), width * 2);
                                });
                            }
                            else
                            {
                                //使用上一行原有数据
                                Parallel.For(0, zoomHeight, h =>
                                {
                                    int originHeightIndex = (int)Math.Floor((double)height * h / (double)zoomHeight);
                            ////使用上一行原有数据
                            //Parallel.For(0, zoomHeight, h =>
                            //{
                            //    int originHeightIndex = (int)Math.Floor((double)height * h / (double)zoomHeight);
                                    byte[] rowBuffer = new byte[width * 4];
                                    Parallel.For(0, width, w =>
                                    {
                                        rowBuffer[w * 4 + 2] = Marshal.ReadByte(bufferPointer, (int)(width * 2 * originHeightIndex + w * 2));
                                        rowBuffer[w * 4 + 3] = Marshal.ReadByte(bufferPointer, (int)(width * 2 * originHeightIndex + w * 2 + 1));
                                    });
                            //    byte[] rowBuffer = new byte[width * 4];
                            //    Parallel.For(0, width, w =>
                            //    {
                            //        rowBuffer[w * 4 + 2] = Marshal.ReadByte(bufferPointer, (int)(width * 2 * originHeightIndex + w * 2));
                            //        rowBuffer[w * 4 + 3] = Marshal.ReadByte(bufferPointer, (int)(width * 2 * originHeightIndex + w * 2 + 1));
                            //    });
                            //    Marshal.Copy(rowBuffer, 0, (IntPtr)((long)zoomPtr + width * 2 * h), rowBuffer.Length);
                            //});
                                    Marshal.Copy(rowBuffer, 0, (IntPtr)((long)zoomPtr + width * 2 * h), rowBuffer.Length);
                                });
                            }
                            if (imgSet != null)
                            {
                                imgSet.HImage = new HImage();
                                imgSet.HImage.GenImage1("uint2", (int)width, zoomHeight, zoomPtr);
                                //imgSet.HImage.GenImage1("uint2", (int)width, zoomHeight, zoomPtr);
                                //imgSet.HImage.GenImage1("int4", (int)width, zoomHeight, zoomPtr);
                                imgSet.HImage.GenImage1(IIConfig.ByteNums == GocatorDataByteNums.Byte2 ? "uint2" : "int4", (int)width, zoomHeight, zoomPtr);
                                imgSet.HImage_2 = new HImage();
                                imgSet.HImage_2.GenImage1("uint2", (int)width, zoomHeight, zoomPtr);
                                imgSet.HImage_2 = imgSet.HImage.Clone();
                                //imgSet.HImage_2.GenImage1("uint2", (int)width, zoomHeight, zoomPtr);
                                //imgSet.HImage_2.GenImage1("int4", (int)width, zoomHeight, zoomPtr);
                                //imgSet.HImage_2.GenImage1(IIConfig.ByteNums == GocatorDataByteNums.Byte2 ? "uint2" : "int4", (int)width, zoomHeight, zoomPtr);
                                LaserScanParam para = new LaserScanParam()
                                {
@@ -289,7 +295,12 @@
                if (!string.IsNullOrWhiteSpace(opConfig.JobName) && _currentJob != opConfig.JobName)
                {
                    _currentJob = sensor.DefaultJob = opConfig.JobName;
                    LogAsync(DateTime.Now, $"{Name}切换当前任务为{opConfig.JobName}", "");
                    sensor.CopyFile(_currentJob, "_live.job");
                    bool isChanged = false;
                    sensor.LoadedJob(ref _currentJob, ref isChanged);
                    LogAsync(DateTime.Now, $"{Name}当前任务{_currentJob}", "");
                }
                sensor.Flush();
@@ -345,18 +356,17 @@
                _currentExposure = (float)sensor.Setup.GetExposure(GoRole.Main);
            }
            _currentJob = sensor.DefaultJob;
            if (!string.IsNullOrWhiteSpace(IIConfig.DefaultJob) && _currentJob != IIConfig.DefaultJob)
            {
                //_currentJob = sensor.DefaultJob = IIConfig.DefaultJob;
                string currentJob = IIConfig.DefaultJob;
                bool isChanged = false;
                sensor.LoadedJob(ref currentJob, ref isChanged);
            bool isChanged = false;
            sensor.LoadedJob(ref _currentJob, ref isChanged);
            LogAsync(DateTime.Now, $"{Name}当前任务{_currentJob}", "");
                if (!isChanged)
                {
                    throw new ProcessException($"{Name}未成功切换至任务{IIConfig.DefaultJob}");
                }
            if (!string.IsNullOrWhiteSpace(IIConfig.DefaultJob) && IIConfig.DefaultJob != _currentJob)
            {
                sensor.CopyFile(IIConfig.DefaultJob, "_live.job");
                //_currentJob = IIConfig.DefaultJob;
                sensor.LoadedJob(ref _currentJob, ref isChanged);
                LogAsync(DateTime.Now, $"{Name}当前任务{_currentJob}", "");
            }
            sensor.Flush();
src/Bro.Device.HikCamera/Bro.Device.HikCamera.csproj
@@ -21,7 +21,7 @@
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <PlatformTarget>x64</PlatformTarget>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
src/Bro.M071.Process/Bro.M071.Process.csproj
@@ -126,12 +126,6 @@
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="UI\KeyIndicator.cs" />
    <Compile Include="UI\M071Node.cs" />
    <Compile Include="UI\M071_DataForm.cs">
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="UI\M071_DataForm.Designer.cs">
      <DependentUpon>M071_DataForm.cs</DependentUpon>
    </Compile>
    <Compile Include="UI\M071_MainForm.cs">
      <SubType>Form</SubType>
    </Compile>
@@ -159,9 +153,6 @@
      <AutoGen>True</AutoGen>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <EmbeddedResource Include="UI\M071_DataForm.resx">
      <DependentUpon>M071_DataForm.cs</DependentUpon>
    </EmbeddedResource>
    <EmbeddedResource Include="UI\M071_MainForm.resx">
      <DependentUpon>M071_MainForm.cs</DependentUpon>
    </EmbeddedResource>
@@ -194,10 +185,6 @@
    <ProjectReference Include="..\Bro.Common.Model\Bro.Common.Model.csproj">
      <Project>{1A3CBFE7-3F78-42C3-95C5-10360450DBEA}</Project>
      <Name>Bro.Common.Model</Name>
    </ProjectReference>
    <ProjectReference Include="..\Bro.M071.DBManager\Bro.M071.DBManager.csproj">
      <Project>{230B0FFF-F6AF-45FE-AAF7-B4B66250DD58}</Project>
      <Name>Bro.M071.DBManager</Name>
    </ProjectReference>
    <ProjectReference Include="..\Bro.M071.Model\Bro.M071.Model.csproj">
      <Project>{AD645C48-5811-4B1E-B81F-D35D5E6B577F}</Project>
src/Bro.M071.Process/M071Config.cs
@@ -1,5 +1,6 @@
using Bro.Common.Base;
using Bro.Common.Helper;
using Bro.Common.Interface;
using Bro.Common.Model;
using Bro.Process;
using System;
@@ -91,13 +92,13 @@
        //[Editor(typeof(FoldDialogEditor), typeof(UITypeEditor))]
        //public string ResultDataSaveFolder { get; set; } = "";
        [Category("图片保存配置")]
        [Category("单键图片保存配置")]
        [Description("单键图片保存配置")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
        public ImageSaveOption ImageSaveOption { get; set; } = new ImageSaveOption();
        [Category("图片保存配置")]
        [Category("单键图片保存配置")]
        [Description("单键图片保存目录路径")]
        [Editor(typeof(FoldDialogEditor), typeof(UITypeEditor))]
        public string ImageSaveFolder { get; set; } = "";
@@ -167,9 +168,63 @@
        [DisplayName("多次验证开关")]
        public bool IsDisableMultipleCheckTimes { get; set; }
        [Category("MES设置")]
        [Description("键名和上传代码映射关系")]
        [DisplayName("键名映射")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<KeyCodeMap>), typeof(UITypeEditor))]
        public List<KeyCodeMap> KeyCodeMappingCollection { get; set; } = new List<KeyCodeMap>();
        [Category("复位设置")]
        [Description("大复位需要信号持续时间,复位信号持续超出该时间执行大复位,单位:s")]
        [DisplayName("大复位持续信号长度")]
        public int FullResetRequiredDuration { get; set; } = 3;
    }
    [Device("Demostration", "Demo操作配置", EnumHelper.DeviceAttributeType.OperationConfig)]
    public class DemostrationOperationConfig : OperationConfigBase, IHalconToolPath
    {
        [Category("组合操作配置集合")]
        [Description("组合操作配置集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<OperationCombination>), typeof(UITypeEditor))]
        public List<OperationCombination> DemoOperations { get; set; } = new List<OperationCombination>();
        public List<string> GetHalconToolPathList()
        {
            return DemoOperations.SelectMany(d => d.GetHalconToolPathList()).ToList();
        }
    }
    public class OperationCombination : IComplexDisplay, IHalconToolPath
    {
        [Category("运动机构配置")]
        [Description("运动机构配置")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(IOperationConfigByDeviceEditor), typeof(UITypeEditor))]
        public DeviceOpBind MotionOp { get; set; } = new DeviceOpBind();
        [Category("相机和操作配置")]
        [Description("相机和操作配置")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(IOperationConfigByDeviceEditor), typeof(UITypeEditor))]
        public DeviceOpBind CameraOp { get; set; } = new DeviceOpBind();
        public string GetDisplayText()
        {
            return MotionOp.GetDisplayText() + "\t" + CameraOp.GetDisplayText();
        }
        public List<string> GetHalconToolPathList()
        {
            if (CameraOp.OpConfig is IHalconToolPath path)
            {
                return path.GetHalconToolPathList();
            }
            else
            {
                return new List<string>();
            }
        }
    }
}
src/Bro.M071.Process/M071Models.cs
@@ -52,7 +52,6 @@
        }
    }
    public class KeyAlgorithem : IComplexDisplay
    {
        [Browsable(false)]
@@ -84,6 +83,10 @@
        [Description("检测算法路径")]
        [Editor(typeof(FileDialogEditor), typeof(UITypeEditor))]
        public string AlgorithemPath { get; set; }
        [Category("上传NG代码")]
        [Description("上传NG代码")]
        public string NGCode { get; set; }
        [Category("显示配置")]
        [Description("显示区域大小")]
@@ -509,6 +512,23 @@
        }
    }
    public class KeyCodeMap : IComplexDisplay
    {
        [Category("键名映射配置")]
        [Description("键名")]
        [TypeConverter(typeof(KeyNameDictConverter))]
        public string Key { get; set; }
        [Category("键名映射配置")]
        [Description("上传对应键代码Code")]
        public string KeyCode { get; set; }
        public string GetDisplayText()
        {
            return $"{Key}--{KeyCode}";
        }
    }
    //public class NoticedDictionary<T1, T2> : Dictionary<T1, T2>, INotifyPropertyChanged
    //{
    //    public event PropertyChangedEventHandler PropertyChanged;
src/Bro.M071.Process/M071Process.cs
@@ -2,7 +2,7 @@
using Bro.Common.Helper;
using Bro.Common.Interface;
using Bro.Common.Model;
using Bro.M071.DBManager;
//using Bro.M071.DBManager;
using Bro.M071.Model;
using Bro.M071.Model.Model;
using Bro.M071.Process.UI;
@@ -361,6 +361,74 @@
            return new ProcessResponse();
        }
        [ProcessMethod("Demostration", "CheckBasePlan", "基准平面检校", InvokeType.TestInvoke)]
        public ProcessResponse CheckBasePlan(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice)
        {
            if (opConfig is DemostrationOperationConfig config)
            {
                config.DemoOperations.ForEach(d =>
                {
                    IDevice device = DeviceCollection.FirstOrDefault(u => u.Id == d.MotionOp.Device);
                    if (device != null)
                    {
                        IMotionCard motionDevice = device as IMotionCard;
                        if (motionDevice != null)
                        {
                            var response = motionDevice.Run(d.MotionOp.OpConfig);
                            if (!response.Result)
                            {
                                throw new ProcessException($"{device.Name}异常,{response.Message}", null, ExceptionLevel.Fatal);
                            }
                        }
                    }
                    CameraBase camera = DeviceCollection.FirstOrDefault(u => u.Id == d.CameraOp.Device) as CameraBase;
                    if (camera != null)
                    {
                        IImageSet set = null;
                        try
                        {
                            set = CollectHImage(camera, d.CameraOp.OpConfig);
                        }
                        catch (ProcessException pEx)
                        {
                            pEx.Level = ExceptionLevel.Fatal;
                            throw pEx;
                        }
                        if (set != null)
                        {
                            LaserScanParam scanParam = JsonConvert.DeserializeObject<LaserScanParam>(set.ImageData);
                            LogAsync(DateTime.Now, $"扫描参数:{set.ImageData}", "");
                            var tool = GetHalconTool(d.CameraOp.OpConfig, config.MonitorSetId);
                            if (tool != null)
                            {
                                using (tool.InputImageDic["INPUT_Image"] = set.HImage)
                                {
                                    tool.InputTupleDic["INPUT_Resolution_Z"] = scanParam.Resolution_Z / 1000000.0;
                                    if (!tool.RunProcedure(out string error))
                                    {
                                        LogAsync(DateTime.Now, $"{tool.ProcedureName}执行异常", error);
                                    }
                                    else
                                    {
                                        var diffX = tool.GetResultTuple("OUTPUT_Result_X").D;
                                        var diffY = tool.GetResultTuple("OUTPUT_Result_Y").D;
                                        LogAsync(DateTime.Now, $"检测平面,X方向极差:{diffX.ToString(_precision)},Y方向极差:{diffY.ToString(_precision)}", "");
                                    }
                                }
                            }
                        }
                    }
                });
            }
            return new ProcessResponse();
        }
        #endregion
        #region 私有方法
@@ -500,44 +568,44 @@
            }
        }
        KeyUnitDataManager keyUnitDataManager = new KeyUnitDataManager();
        MeasurementUnitResultManager measurementUnitResultManager = new MeasurementUnitResultManager();
        MeasurementAndKeyDataRelationManager measurementAndKeyDataRelationManager = new MeasurementAndKeyDataRelationManager();
        ProductionMeasurementRecordsManager productionMeasurementRecordsManager = new ProductionMeasurementRecordsManager();
        //KeyUnitDataManager keyUnitDataManager = new KeyUnitDataManager();
        //MeasurementUnitResultManager measurementUnitResultManager = new MeasurementUnitResultManager();
        //MeasurementAndKeyDataRelationManager measurementAndKeyDataRelationManager = new MeasurementAndKeyDataRelationManager();
        //ProductionMeasurementRecordsManager productionMeasurementRecordsManager = new ProductionMeasurementRecordsManager();
        static object dataSaveLock = new object();
        private async void SaveProductionData(ProductionMeasurementUnitResultAndKeyUnitDataSet measurementUnitResultAndKeyUnitDataSet)
        {
            await Task.Run(() =>
            {
                try
                {
                    lock (dataSaveLock)
                    {
                        // 获取 产品数据 并保存
                        var productionMeasurementRecords = measurementUnitResultAndKeyUnitDataSet.ProductionMeasurementRecord;
                        productionMeasurementRecordsManager.CreateModel(productionMeasurementRecords);
        //private async void SaveProductionData(ProductionMeasurementUnitResultAndKeyUnitDataSet measurementUnitResultAndKeyUnitDataSet)
        //{
        //    await Task.Run(() =>
        //    {
        //        try
        //        {
        //            lock (dataSaveLock)
        //            {
        //                // 获取 产品数据 并保存
        //                var productionMeasurementRecords = measurementUnitResultAndKeyUnitDataSet.ProductionMeasurementRecord;
        //                productionMeasurementRecordsManager.CreateModel(productionMeasurementRecords);
                        // 获取 原始数据 并保存
                        var keyUnitDatas = measurementUnitResultAndKeyUnitDataSet.KeyUnitDataList;
                        keyUnitDataManager.BatchAddKeyUnitData(keyUnitDatas);
        //                // 获取 原始数据 并保存
        //                var keyUnitDatas = measurementUnitResultAndKeyUnitDataSet.KeyUnitDataList;
        //                keyUnitDataManager.BatchAddKeyUnitData(keyUnitDatas);
                        // 获取 检测结果数据 并保存
                        var measurementUnitResults = measurementUnitResultAndKeyUnitDataSet.MeasurementUnitResultList;
                        measurementUnitResultManager.BatchAddMeasurementUnitResult(measurementUnitResults);
        //                // 获取 检测结果数据 并保存
        //                var measurementUnitResults = measurementUnitResultAndKeyUnitDataSet.MeasurementUnitResultList;
        //                measurementUnitResultManager.BatchAddMeasurementUnitResult(measurementUnitResults);
                        // 获取 关系数据并保存
                        var measurementAndKeyDataRelationList = measurementUnitResultAndKeyUnitDataSet.MeasurementAndKeyDataRelationList;
                        measurementAndKeyDataRelationManager.BatchAddMeasurementAndKeyDataRelation(measurementAndKeyDataRelationList);
                    }
                }
                catch (Exception ex)
                {
                    LogAsync(DateTime.Now, "数据保存异常", ex.GetExceptionMessage());
                }
            });
        }
        //                // 获取 关系数据并保存
        //                var measurementAndKeyDataRelationList = measurementUnitResultAndKeyUnitDataSet.MeasurementAndKeyDataRelationList;
        //                measurementAndKeyDataRelationManager.BatchAddMeasurementAndKeyDataRelation(measurementAndKeyDataRelationList);
        //            }
        //        }
        //        catch (Exception ex)
        //        {
        //            LogAsync(DateTime.Now, "数据保存异常", ex.GetExceptionMessage());
        //        }
        //    });
        //}
        private ProductionMeasurementUnitResultAndKeyUnitDataSet GetMeasurementUnitResultAndKeyUnitData(ProductionMeasurement pData)
        {
@@ -620,49 +688,49 @@
            return measurementUnitResultAndKeyUnitDataSet;
        }
        private async void ExportProductionExcel(ProductionMeasurementUnitResultAndKeyUnitDataSet measurementUnitResultAndKeyUnitDataSet)
        {
            if (!Config.IsCSVOutputEnabled)
                return;
        //private async void ExportProductionExcel(ProductionMeasurementUnitResultAndKeyUnitDataSet measurementUnitResultAndKeyUnitDataSet)
        //{
        //    if (!Config.IsCSVOutputEnabled)
        //        return;
            await Task.Run(() =>
            {
                ExcelExportSet excelExportDto = new ExcelExportSet();
                excelExportDto.Worksheets = new List<string>() { "原始数据", "检测结果" };
                var keyUnitColumns = new Dictionary<string, string>()
                {
                    {"ProductionBarcode", "产品条码"},
                    {"Key", "键"},
                    {"MeasurementItem", "检测项"},
                    {"ItemValue", "检测值"}
                };
                var measurementUnitResultColumns = new Dictionary<string, string>()
                {
                    {"ProductionBarcode", "产品条码"},
                    {"MeasurementName", "检测名称"},
                    {"MeasurementType", "检测类型"},
                    {"MeasurementValue", "检测值"},
                    {"MeasurementResult", "检测结果"},
                };
                excelExportDto.WorksheetColumns[excelExportDto.Worksheets[0]] = keyUnitColumns;
                excelExportDto.WorksheetColumns[excelExportDto.Worksheets[1]] = measurementUnitResultColumns;
        //    await Task.Run(() =>
        //    {
        //        ExcelExportSet excelExportDto = new ExcelExportSet();
        //        excelExportDto.Worksheets = new List<string>() { "原始数据", "检测结果" };
        //        var keyUnitColumns = new Dictionary<string, string>()
        //        {
        //            {"ProductionBarcode", "产品条码"},
        //            {"Key", "键"},
        //            {"MeasurementItem", "检测项"},
        //            {"ItemValue", "检测值"}
        //        };
        //        var measurementUnitResultColumns = new Dictionary<string, string>()
        //        {
        //            {"ProductionBarcode", "产品条码"},
        //            {"MeasurementName", "检测名称"},
        //            {"MeasurementType", "检测类型"},
        //            {"MeasurementValue", "检测值"},
        //            {"MeasurementResult", "检测结果"},
        //        };
        //        excelExportDto.WorksheetColumns[excelExportDto.Worksheets[0]] = keyUnitColumns;
        //        excelExportDto.WorksheetColumns[excelExportDto.Worksheets[1]] = measurementUnitResultColumns;
                excelExportDto.WorksheetDataTable[excelExportDto.Worksheets[0]] = ExcelExportHelper.ListToDataTable(measurementUnitResultAndKeyUnitDataSet.KeyUnitDataList, keyUnitColumns);
                excelExportDto.WorksheetDataTable[excelExportDto.Worksheets[1]] = ExcelExportHelper.ListToDataTable(measurementUnitResultAndKeyUnitDataSet.MeasurementUnitResultList, measurementUnitResultColumns); ;
        //        excelExportDto.WorksheetDataTable[excelExportDto.Worksheets[0]] = ExcelExportHelper.ListToDataTable(measurementUnitResultAndKeyUnitDataSet.KeyUnitDataList, keyUnitColumns);
        //        excelExportDto.WorksheetDataTable[excelExportDto.Worksheets[1]] = ExcelExportHelper.ListToDataTable(measurementUnitResultAndKeyUnitDataSet.MeasurementUnitResultList, measurementUnitResultColumns); ;
                if (!Directory.Exists(Config.LogPath))
                {
                    Directory.CreateDirectory(Config.LogPath);
                }
                var fileName = Path.Combine(Config.LogPath, $"ProductionData_{DateTime.Now.ToString("yyyyMMdd")}.xlsx");
        //        if (!Directory.Exists(Config.LogPath))
        //        {
        //            Directory.CreateDirectory(Config.LogPath);
        //        }
        //        var fileName = Path.Combine(Config.LogPath, $"ProductionData_{DateTime.Now.ToString("yyyyMMdd")}.xlsx");
                byte[] filecontent = ExcelExportHelper.CreateOrAppendExcel(excelExportDto, fileName);
                FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
                fs.Write(filecontent, 0, filecontent.Length);
                fs.Flush();
                fs.Close();
            });
        }
        //        byte[] filecontent = ExcelExportHelper.CreateOrAppendExcel(excelExportDto, fileName);
        //        FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
        //        fs.Write(filecontent, 0, filecontent.Length);
        //        fs.Flush();
        //        fs.Close();
        //    });
        //}
        static object excelExportLock = new object();
        private async void ExportProductionInColumns(ProductionMeasurementUnitResultAndKeyUnitDataSet exportData)
@@ -815,7 +883,7 @@
                        #endregion
                        #region Slant
                        var slantMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType == "Slant").ToList();
                        var slantMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType.StartsWith("Slant")).ToList();
                        if (slantMeasures.Count > 0)
                        {
                            int slantStartCol = slantSheet.Dimension.Columns;
@@ -854,7 +922,7 @@
                        #region Alignment
                        {
                            var alignmentMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType == "Alignment").ToList();
                            var alignmentMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType.StartsWith("Alignment")).ToList();
                            if (alignmentMeasures.Count > 0)
                            {
                                List<string> keysList = new List<string>();
@@ -913,7 +981,7 @@
                        #region RowAlignment
                        {
                            var rowAlignmentMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType == "RowAlignment").ToList();
                            var rowAlignmentMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType.StartsWith("RowAlignment")).ToList();
                            if (rowAlignmentMeasures.Count > 0)
                            {
                                List<string> keysList = new List<string>();
src/Bro.M071.Process/M071Process_MES.cs
@@ -82,14 +82,14 @@
        {
            get
            {
                if (incomingCheckMethod == null)
                if (dataUploadMethod == null)
                {
                    incomingCheckMethod = InitialMESWebServiceMethod(DataUploadMethodName, out DataUploadObj);
                    dataUploadMethod = InitialMESWebServiceMethod(DataUploadMethodName, out DataUploadObj);
                }
                return incomingCheckMethod;
                return dataUploadMethod;
            }
            set => incomingCheckMethod = value;
            set => dataUploadMethod = value;
        }
        private MethodInfo InitialMESWebServiceMethod(string methodName, out object invokeClass)
@@ -138,7 +138,7 @@
            //string paraStr = JsonConvert.SerializeObject(paras);
            //LogAsync(DateTime.Now, $"MES入料检测 {url}", paraStr);
            IncomingCheckMethod = InitialMESWebServiceMethod(IncomingCheckMethodName, out IncomingCheckObj);
            //IncomingCheckMethod = InitialMESWebServiceMethod(IncomingCheckMethodName, out IncomingCheckObj);
            Stopwatch sw = new Stopwatch();
            sw.Start();
@@ -178,7 +178,7 @@
            //string paraStr = JsonConvert.SerializeObject(paras);
            //LogAsync(DateTime.Now, $"MES数据上传 {url}", paraStr);
            DataUploadMethod = InitialMESWebServiceMethod(DataUploadMethodName, out DataUploadObj);
            //DataUploadMethod = InitialMESWebServiceMethod(DataUploadMethodName, out DataUploadObj);
            Stopwatch sw = new Stopwatch();
            sw.Start();
@@ -195,7 +195,8 @@
                pMeasure.PResult == "OK" ? "PASS" : "FAIL",
                Config.LineCode,
                GetMESSlantData(pMeasure),
                GetMESAlignmentData(pMeasure)
                GetMESAlignmentData(pMeasure),
                GetNGKeys(pMeasure)
            };
            //LogAsync(DateTime.Now, $"{barcode}产品检测数据上传", JsonConvert.SerializeObject(paras));
@@ -210,12 +211,59 @@
        private string GetMESAlignmentData(ProductionMeasurement pMeasure)
        {
            return string.Join(",", pMeasure.Measurements.Where(u => u.MeasureType == "Alignment").Select(u => $"{u.Name}:{u.Spec.ActualValue.Value.ToString(_precision)}"));
            return string.Join(",", pMeasure.Measurements.Where(u => u.MeasureType.StartsWith("Alignment")).Select(u => $"{u.Name}:{u.Spec.ActualValue.Value.ToString(_precision)}"));
        }
        private string GetMESSlantData(ProductionMeasurement pMeasure)
        {
            return string.Join(",", pMeasure.Measurements.Where(u => u.MeasureType == "Slant").Select(u => $"{u.Name}:{u.Spec.ActualValue.Value.ToString(_precision)}"));
            return string.Join(",", pMeasure.Measurements.Where(u => u.MeasureType.StartsWith("Slant")).Select(u => $"{u.Name}:{u.Spec.ActualValue.Value.ToString(_precision)}"));
        }
        private string GetNGKeys(ProductionMeasurement pMeasure)
        {
            string ngCodes = "";
            Dictionary<string, List<string>> ngCodeDict = new Dictionary<string, List<string>>();
            var ngList = pMeasure.Measurements.Where(m => m.Spec.MeasureResult != true).ToList();
            if (ngList.Count == 0)
                return "";
            ngList.GroupBy(m => m.MeasureType).ToList().ForEach(g =>
                  {
                      var mType = Config.MeasureTypeCollection.FirstOrDefault(u => u.Code == g.Key);
                      if (mType != null)
                      {
                          if (!ngCodeDict.ContainsKey(mType.NGCode))
                          {
                              ngCodeDict[mType.NGCode] = new List<string>();
                          }
                          g.ToList().ForEach(k =>
                          {
                              k.KeyUnitCollection.Select(kk => kk.Key).ToList().ForEach(key =>
                              {
                                  string uploadKeyCode = key;
                                  var keyCodeMap = Config.KeyCodeMappingCollection.FirstOrDefault(kcm => kcm.Key == key);
                                  if (keyCodeMap != null)
                                  {
                                      uploadKeyCode = keyCodeMap.KeyCode;
                                  }
                                  if (!ngCodeDict[mType.NGCode].Contains(uploadKeyCode))
                                  {
                                      ngCodeDict[mType.NGCode].Add(uploadKeyCode);
                                  }
                              });
                          });
                      }
                  });
            if (ngCodeDict.Count > 0)
            {
                ngCodes = string.Join(";", ngCodeDict.ToList().Select(u => $"{u.Key}:{string.Join(",", u.Value)}"));
            }
            return ngCodes;
        }
    }
src/Bro.M071.Process/UI/KeyIndicator.cs
@@ -157,7 +157,7 @@
            return Math.Abs(p.X - (DisplayRect.X + DisplayRect.Width)) < (DisplayRect.Width / 3) && Math.Abs(p.Y - (DisplayRect.Y + DisplayRect.Height)) < (DisplayRect.Height / 3);
        }
        int x, y = 0;
        //int x, y = 0;
        public override void StretchBottom(Point p)
        {
            if (p.Y > DisplayRect.Y)
src/Bro.M071.Process/UI/M071_MainForm.cs
@@ -317,6 +317,10 @@
            {
                _barcode += keyStr.Substring(1).ToUpper();
            }
            else if (keyStr.StartsWith("NumPad"))
            {
                _barcode += keyStr.Replace("NumPad", "");
            }
            if (e.KeyValue == 13)
            {
src/Bro.Process/ProcessControl.cs
@@ -21,7 +21,7 @@
using System.Threading.Tasks;
using static Bro.Common.Helper.EnumHelper;
namespace Bro.Process
namespace Bro.Process
{
    [Process("", DeviceAttributeType.Device)]
    public partial class ProcessControl : IProcess
@@ -470,14 +470,14 @@
        /// <param name="config">操作配置,用来确认监听来源和算法路径</param>
        /// <param name="algorithemPath">算法路径,默认是配置中的第一个算法路径</param>
        /// <returns>Halcon算法</returns>
        protected HDevEngineTool GetHalconTool(IOperationConfig config, string algorithemPath = "")
        protected HDevEngineTool GetHalconTool(IOperationConfig config, string monitorSetId = "", string algorithemPath = "")
        {
            if (string.IsNullOrWhiteSpace(algorithemPath))
            {
                algorithemPath = (config as IHalconToolPath)?.GetHalconToolPathList()[0];
            }
            string key = config.MonitorSetId + "|" + algorithemPath;
            string key = (string.IsNullOrWhiteSpace(monitorSetId) ? config.MonitorSetId : monitorSetId) + "|" + algorithemPath;
            if (_halconToolDict.ContainsKey(key))
            {
                return _halconToolDict[key];
@@ -631,7 +631,7 @@
        #endregion
        #region 图像处理
        protected IImageSet CollectHImage(CameraBase camera, IOperationConfig opConfig, [CallerMemberName]string methodCode = "")
        protected IImageSet CollectHImage(CameraBase camera, IOperationConfig opConfig, [CallerMemberName] string methodCode = "")
        {
            IImageSet set = null;
src/ExcelTest/ExcelTest.csproj
@@ -80,10 +80,6 @@
    <None Include="App.config" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\Bro.M071.DBManager\Bro.M071.DBManager.csproj">
      <Project>{230B0FFF-F6AF-45FE-AAF7-B4B66250DD58}</Project>
      <Name>Bro.M071.DBManager</Name>
    </ProjectReference>
    <ProjectReference Include="..\Bro.M071.Model\Bro.M071.Model.csproj">
      <Project>{AD645C48-5811-4B1E-B81F-D35D5E6B577F}</Project>
      <Name>Bro.M071.Model</Name>