• EQ控制卡二次开发(火凤凰、蓝精灵异步单双色控制卡卡)


         在工业信息化行业,少不了生产可视化的模块,其中应用最多的是采用LED屏的方式,通过软件控制屏幕展示相关的生产计划完成状态,工位的状态,产线的运行状态,以及相关自动化设备的状态等,这就要求通信实时性,准确性。LED屏控制核心在于控制卡目前市场上各种控制卡种类繁多,今天先介绍下EQ系列的控制卡吧。其实通信实时性要求采用同步卡效果最佳,但是苦于现场不想增加专用的控制电脑(大部分同步卡原理即抓屏模式,实时展现电脑桌面的内容,电脑不能同时进行其他操作),所以决定采用异步卡异步通信,直接在后台通过程序建立和LED屏幕的连接,实时发送数据给LED屏幕。此篇就讲下根据官方二次开发包开发的问题。

        EQ官方二次开发包

        此处我们采用的是网络通讯的方式,即EQ控制卡上需固定IP,用以识别并和控制程序建立连接。

        EQ2008_Dll.dll是官方提供的开发接口,其中的接口方法包含如下:

    using System;
    using System.Runtime.InteropServices;
    using ServiceCloud.Devices.Led.EQ2008.Parameters;
    
    namespace ServiceCloud.Devices.Led.EQ2008
    {
        /*
         本动态库接口适用于:EQ火凤凰系列和蓝精灵系列控制器!
         火凤凰系列:EQ2013、EQ2023、EQ2033
         蓝精灵系列:EQ2012、EQ2011、EQ2008-1/2E、EQ2008-M
         */
        internal class LedControllerHandler
        {
            # region 1.节目操作函数
            /// <summary>
            /// 添加节目
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="bWaitToEnd">TRUE 等待节目播放完成再播放下个节目,FALSE 节目播放时间为 iPlayTime</param>
            /// <param name="iPlayTime">节目播放时间,单位为秒</param>
            /// <returns>节目索引号</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern int User_AddProgram(int CardNum, Boolean bWaitToEnd, int iPlayTime);
            /// <summary>
            /// 删除一个节目
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="iProgramIndex">节目索引号</param>
            /// <returns>0-删除失败,1-删除成功</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_DelProgram(int CardNum, int iProgramIndex);
            /// <summary>
            /// 删除所有节目
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <returns>0-删除失败,1-删除成功</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_DelAllProgram(int CardNum);
            /// <summary>
            /// 添加文本区
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="pText">文本参数表指针,参考【参数表】中 9</param>
            /// <param name="iProgramIndex">节目索引号</param>
            /// <returns>-1-添加文本区失败,非-1-分区编号</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern int User_AddText(int CardNum, ref User_Text pText, int iProgramIndex);
            /// <summary>
            /// 添加单行文本区
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="pSingleText">单行文本参数表指针,参考【参数表】中 8</param>
            /// <param name="iProgramIndex">节目索引号</param>
            /// <returns>-1-添加单行文本区失败,非-1-分区编号</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern int User_AddSingleText(int CardNum, ref User_SingleText pSingleText, int iProgramIndex);
            /// <summary>
            /// 添加图文区
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="pBmp">图文区参数表指针,参考【参数表】中 7</param>
            /// <param name="iProgramIndex">节目索引号</param>
            /// <returns>-1-添加图文区失败,非-1-分区编号</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern int User_AddBmpZone(int CardNum, User_Bmp pBmp, int iProgramIndex);
            /// <summary>
            /// 指定图像句柄添加图片
            /// </summary>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_AddBmp(int CardNum, int iBmpPartNum, IntPtr hBitmap, ref User_MoveSet pMoveSet, int iProgramIndex);
            /// <summary>
            /// 指定图像路径添加图片
            /// </summary>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_AddBmpFile(int CardNum, int iBmpPartNum, string strFileName, ref User_MoveSet pMoveSet, int iProgramIndex);
            /// <summary>
            /// 添加时间区
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="pdateTime">时间参数表指针,参考【参数表】中 6</param>
            /// <param name="iProgramIndex">节目索引号</param>
            /// <returns>-1-添加时间区失败,非-1-分区编号</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern int User_AddTime(int CardNum, ref User_DateTime pdateTime, int iProgramIndex);
            /// <summary>
            /// 添加倒计时区
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="pTimeCount">倒计时参数表指针,参考【参数表】中 4</param>
            /// <param name="iProgramIndex">节目索引号</param>
            /// <returns>-1-添加计时区失败,非-1-分区编号</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern int User_AddTimeCount(int CardNum, User_Timer pTimeCount, int iProgramIndex);
            /// <summary>
            /// 添加温度区
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="pTemperature">温度参数表指针,参考【参数表】中 5</param>
            /// <param name="iProgramIndex">节目索引号</param>
            /// <returns>-1-添加温度区失败,非-1-分区编号</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern int User_AddTemperature(int CardNum, User_Temperature pTemperature, int iProgramIndex);
            /// <summary>
            /// 添加 RTF 文件区
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="pRTFt">RTF 文件参数表指针,参考【参数表】中 10</param>
            /// <param name="iProgramIndex">节目索引号</param>
            /// <returns>-1-添加文本区失败,非-1-分区编号</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern int User_AddRTF(int CardNum, User_RTF pRTFt, int iProgramIndex);
            /// <summary>
            /// 向控制器发送数据
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <returns>FALSE - 发送失败,TRUE - 发送成功</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_SendToScreen(int CardNum);
            #endregion
    
            #region 2.实时发送数据(高频率发送)
            //实时建立连接
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_RealtimeConnect(int CardNum);
            //实时关闭连接
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_RealtimeDisConnect(int CardNum);
            //实时发送图片数据
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_RealtimeSendData(int CardNum, int x, int y, int iWidth, int iHeight, IntPtr hBitmap);
            //实时发送图片文件
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern Boolean User_RealtimeSendBmpData(int CardNum, int x, int y, int iWidth, int iHeight, string strFileName);
            //实时发送文本
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern Boolean User_RealtimeSendText(int CardNum, int x, int y, int iWidth, int iHeight, string strText, ref User_FontSet pFontInfo);
            //实时发送清屏
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern Boolean User_RealtimeScreenClear(int CardNum);
            #endregion
    
            #region 3.显示屏控制函数组
            /// <summary>
            /// 校正时间
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <returns>FALSE - 板卡校正时间失败,TRUE - 板卡校正时间成功</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern Boolean User_AdjustTime(int CardNum);
            /// <summary>
            /// 打开显示屏
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <returns>FALSE - 打开显示屏失败,TRUE - 打开显示屏成功</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_OpenScreen(int CardNum);
            /// <summary>
            /// 关闭显示屏
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <returns>FALSE - 关闭显示屏失败,TRUE - 关闭显示屏成功</returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern bool User_CloseScreen(int CardNum);
            /// <summary>
            /// 亮度调节
            /// </summary>
            /// <param name="CardNum">控制卡地址,基数为 1,即第一块控制卡地址为 1</param>
            /// <param name="iLightDegreen">屏幕亮度值</param>
            /// <returns></returns>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern Boolean User_SetScreenLight(int CardNum, int iLightDegreen);
            /// <summary>
            /// Reload参数文件
            /// </summary>
            /// <param name="strEQ2008_Dll_Set_Path"></param>
            [DllImport("EQ2008_Dll.dll", CharSet = CharSet.Ansi)]
            public static extern void User_ReloadIniFile(string strEQ2008_Dll_Set_Path);
            #endregion
    
            [DllImport("gdi32.dll")]
            public static extern bool DeleteObject(IntPtr hObject);
        }
    }
    View Code

        EQ2008_Dll_Set.ini是LED屏幕的通信的配置文件,在屏幕初始化之前就要存在,可手动配置好放至程序运行的根目录,为了可复用性,在此将其在程序配置【通信地址(ip),端口号(port),屏幕大小(screenHeight,screenWidth),单双色(colorStyle),控制卡型号(cardType)】,在程序运行前(即Program.cs文件中动态生成EQ2008_Dll_Set.ini文件)

    var screens = BLL.Common.DataSetCache.Screens;
                foreach (var screen in screens)
                {
                    if (screen.screenType == Setting.屏幕类型.主控屏)
                        ServiceCloud.Devices.Led.EQ2008.LedController.Register(CardType.EQ2023, ScreenColorStyle.Multicolor, 224, 1024, screen.ip, 5005);
                    if (screen.screenType == Setting.屏幕类型.分装区屏)
                        ServiceCloud.Devices.Led.EQ2008.LedController.Register(CardType.EQ2023, ScreenColorStyle.Multicolor, 192, 192, screen.ip, 5005);
                    if (screen.screenType == Setting.屏幕类型.配发区屏)
                        ServiceCloud.Devices.Led.EQ2008.LedController.Register(CardType.EQ2023, ScreenColorStyle.Multicolor, 64, 384, screen.ip, 5005);
                }
                ServiceCloud.Devices.Led.EQ2008.LedController.Initialize();
    View Code

        在生成控制卡的通信配置文件以后再程序里面(如下图)

    通过点击开始连接按钮(按钮事件如下)

     private void OpenConnect()
            {
                try
                {
                    var index = 0;
                    var screenNumbers = GetCheckValues(false);
                    if (screenNumbers.Count <= 0)
                    {
                        MessageBoard.Error(this, "请先勾选您所要操作的LED屏");
                        return;
                    }
                    foreach (var screenNumber in screenNumbers)
                    {
                        var screenModel = DataSetCache.Screens.FirstOrDefault(o => o.number == screenNumber);
                        var ledBasics = DataSetCache.LedBasics.FirstOrDefault(o => o.ScreenModel.ip == screenModel.ip);
                        if (ledBasics.ScreenState == EnumScreenState.Open)
                            continue;
    
                        if (!ledBasics.ScreenModel.ip.PingIp())
                        {
                            var message = string.Format("{0}-启动失败!可能原因:未找到当前屏幕的通信地址,请检查当前屏幕网络通信。", ledBasics.ScreenModel.name);
                            MSGLSBOX(message);
                            Logger.Error(message);
                            continue;
                        }
                        var controller = ServiceCloud.Devices.Led.EQ2008.LedController.GetFromIP(ledBasics.ScreenModel.ip);
                        if (controller == null)
                        {
                            var message = string.Format("{0}-启动失败!可能原因:屏幕初始化失败。", ledBasics.ScreenModel.name);
                            MSGLSBOX(message);
                            Logger.Error(message);
                            continue;
                        }
                        else if (!controller.Open())
                        {
                            var message = string.Format("{0}-启动失败!可能原因:屏幕打开失败。", ledBasics.ScreenModel.name);
                            MSGLSBOX(message);
                            Logger.Error(message);
                            continue;
                        }
                        else
                        {
                            ledBasics.ScreenState = EnumScreenState.Open;
                            if (ledBasics.ScreenModel.screenType == Setting.屏幕类型.主控屏)
                            {
                                ledBasics.LedThread = new Thread(new ParameterizedThreadStart(LedMainDrive));
                                ledBasics.LedThread.Start(ledBasics);
                            }
                            else if (ledBasics.ScreenModel.screenType == Setting.屏幕类型.分装区屏)
                            {
                                ledBasics.LedThread = new Thread(new ParameterizedThreadStart(LedPackingDrive));
                                ledBasics.LedThread.Start(ledBasics);
                            }
                            else if (ledBasics.ScreenModel.screenType == Setting.屏幕类型.配发区屏)
                            {
                                if (index == 0)
                                    ledBasics.LedThread = new Thread(new ParameterizedThreadStart(LedDistributionDriveN4_1));
                                if (index == 1)
                                    ledBasics.LedThread = new Thread(new ParameterizedThreadStart(LedDistributionDriveN4_2));
                                if (index == 2)
                                    ledBasics.LedThread = new Thread(new ParameterizedThreadStart(LedDistributionDriveN4_3));
                                index++;
                                ledBasics.LedThread.Start(ledBasics);
                            }
                            else
                                MessageBoard.Error(this, string.Format("未识别{0}的屏幕类型", ledBasics.ScreenModel.name));
                        }
                    }
                    RefreshScreenList();
                }
                catch
                {
                    throw;
                }
            }
    View Code

    通过点击断开连接按钮(按钮事件如下)

    private void CloseConnect()
            {
                try
                {
                    var screenNumbers = GetCheckValues(false);
                    if (screenNumbers.Count <= 0)
                    {
                        MessageBoard.Error(this, "请先勾选所要操作的LED屏");
                        return;
                    }
    
                    foreach (var screenNumber in screenNumbers)
                    {
                        var screenModel = DataSetCache.Screens.FirstOrDefault(o => o.number == screenNumber);
                        var ledBasics = DataSetCache.LedBasics.FirstOrDefault(o => o.ScreenModel == screenModel);
                        var controller = ServiceCloud.Devices.Led.EQ2008.LedController.GetFromIP(ledBasics.ScreenModel.ip);
                        if (controller.Close())
                        {
                            if (ledBasics.LedThread != null)
                            {
                                Thread.Sleep(1000);
                                ledBasics.LedThread.Abort();
                                ledBasics.LedThread.Join();
                            }
                            ledBasics.ScreenState = EnumScreenState.Close;
                            RefreshScreenList();
                            MessageBoard.Info(this, "已成功关闭连接!");
                        }
                        else
                            MSGLSBOX(string.Format("{0} 连接关闭失败。", ledBasics.ScreenModel.name));
                    }
                }
                catch
                {
                    throw;
                }
            }
    View Code

    因为当前项目需要控制多个LED屏幕,所以此处采取独立线程去分别控制显示,以下是一个线程内的控制通信的代码

    public void LedPackingDrive(object source)
            {
    
                while (true)
                {
                    try
                    {
                        var ledBasics = source as LedBasics;
                        var packingScreenTwoSwitchingTime = Sys_SystemInfoManager.packingScreenTwoSwitchingTime * 2;
                        var packingScreenOneSwitchingTime = Sys_SystemInfoManager.packingScreenOneSwitchingTime * 2;
                        this.LedControllerForPackingBasic = ServiceCloud.Devices.Led.EQ2008.LedController.GetFromIP(ledBasics.ScreenModel.ip);
                        this.LedControllerForPacking = new LedController.LedControllerForPacking(this.LedControllerForPackingBasic);
                        if (indexForPacking <= packingScreenTwoSwitchingTime)
                            SendToPackingLed(ledBasics, false);
                        else if (indexForPacking > packingScreenTwoSwitchingTime &&
                                 indexForPacking < packingScreenTwoSwitchingTime + packingScreenOneSwitchingTime)
                            SendToPackingLed(ledBasics, true);
                        else if (indexForPacking >= packingScreenTwoSwitchingTime + packingScreenOneSwitchingTime)
                        {
                            indexForPacking = 0;
                            continue;
                        }
                        indexForPacking++;
                        Thread.Sleep(500);
                    }
                    catch (System.Exception ex)
                    {
                        Logger.Error(ex);
                    }
                }
            }
            private void SendToPackingLed(LedBasics ledBasics, bool isSecondPages)
            {
                try
                {
                    var dataItem = new LedController.LedControllerForPacking.ScreenDataItemForPacking();
    
                    List<Obj_TaskManager> tasksForLackMaterial;
                    var tasksForAll = ledBasics.Tasks;
    
                    tasksForLackMaterial = (from task in tasksForAll
                                            where (task.taskState == Setting.作业状态.待执行 || task.taskState == Setting.作业状态.分拣中) &&
                                                   task.completeKit == Setting.齐套状态.缺料
                                            select task).OrderBy(o => o.sequence).ToList();
    
                    if (tasksForLackMaterial.Count > 0)
                    {
                        dataItem.WorkNumber1 = tasksForLackMaterial[0].workNumber;
                        dataItem.Line1 = tasksForLackMaterial[0].line;
    
                        dataItem.WorkNumber2 = tasksForLackMaterial.Count > 1 ? tasksForLackMaterial[1].workNumber : "No Task";
                        dataItem.Line2 = tasksForLackMaterial.Count > 1 ? tasksForLackMaterial[1].line : "No Task";
    
                        var currentTaskChildMaterialCount1 = tasksForLackMaterial[0].CurrentTaskChildMaterialCount.Select(o => string.Format("{0}{1}", RemoveMaterialMemoEnglish(o.childMaterialMemo), o.childMaterialCount)).ToArray().Join(",");
                        if (scroll_Packing1 >= currentTaskChildMaterialCount1.Length)
                        { dataItem.ScrollContent1 = currentTaskChildMaterialCount1; scroll_Packing1 = 0; }
                        else
                        { dataItem.ScrollContent1 = currentTaskChildMaterialCount1.Substring(scroll_Packing1, currentTaskChildMaterialCount1.Length - scroll_Packing1); scroll_Packing1++; }
    
                        if (tasksForLackMaterial.Count > 1)
                        {
                            var currentTaskChildMaterialCount2 = tasksForLackMaterial[1].CurrentTaskChildMaterialCount.Select(o => string.Format("{0}{1}", RemoveMaterialMemoEnglish(o.childMaterialMemo), o.childMaterialCount)).ToArray().Join(",");
                            if (scroll_Packing2 >= currentTaskChildMaterialCount2.Length)
                            { dataItem.ScrollContent2 = currentTaskChildMaterialCount2; scroll_Packing2 = 0; }
                            else
                            { dataItem.ScrollContent2 = currentTaskChildMaterialCount2.Substring(scroll_Packing2, currentTaskChildMaterialCount2.Length - scroll_Packing2); scroll_Packing2 = scroll_Packing2 + 2; }
                        }
    
                        if (isSecondPages)
                            this.LedControllerForPacking.SendToScreen(dataItem, EnumScreenPackingType.ScreenDataForScrollContent);
                        else
                            this.LedControllerForPacking.SendToScreen(dataItem, EnumScreenPackingType.ScreenImageForTask);
                    }
                    else
                        this.LedControllerForPacking.SendToScreen(dataItem, EnumScreenPackingType.ScreenImageForNoTask);
                }
                catch
                {
                    throw;
                }
            }
    View Code

    其中

    this.LedControllerForPacking.SendToScreen(dataItem, EnumScreenPackingType.ScreenImageForNoTask);

    就是往控制卡(屏幕)中发送数据。此处只是简单的介绍下开发包的使用,以及简单设计。

  • 相关阅读:
    一个空类会生成哪些默认函数
    What is VMR(Video Mixing Render)From MSDN
    DirectX backface culling(背面剔除)
    D3DPOOL(资源池)
    两道概率题供大家周末把玩
    空间两点间的距离
    n != n, n == n
    C++默认参数
    D3DPT_TRIANGLELIST与D3DPT_TRIANGLESTRIP
    D3D中的设备丢失
  • 原文地址:https://www.cnblogs.com/mr-meng/p/5833207.html
Copyright © 2020-2023  润新知