VS与雷赛PLC通过OPC通信

硬件软件版本

雷赛MC508CS MC508CS

VS2022 Visual Studio 2022

Win64 OpenSSL v3.5.1 OpenSSL

UaExpert v1.7.2 UaExpert

C++ based OPC UA Client/Server + Pub/Sub SDK - Windows 64Bit C++ SDK

配置

VS运行库选用多线程 DLL (/MD),配置Release x64。

Add Include Directories
Add the following include paths to your application
将Win64 OpenSSL v3.5.1的安装路径D:\Program Files\OpenSSL-Win64\include复制到…\third-party\win64\vs2022

$(SolutionDir)\include\uastack
$(SolutionDir)\include\uabasecpp
$(SolutionDir)\include\uaclientcpp
$(SolutionDir)\include\uapkicpp
$(SolutionDir)\include\xmlparsercpp
$(SolutionDir)\third-party\win64\vs2022
$(SolutionDir)\third-party\win64\vs2022\libxml2\include\libxml	
$(SolutionDir)\utilities

Add Linker Settings
For Additional Library Directories enter the following values:选择Debug或Release。
将安装路径的D:\Program Files\OpenSSL-Win64\lib\VC\x86\MD中的文件复制到…\third-party\win64\vs2022\openssl

$(SolutionDir)\lib
$(SolutionDir)\third-party\win64\vs2022\libxml2\out32dll
$(SolutionDir)\third-party\win64\vs2022\openssl

For Additional Dependencies (Debug) enter:根据配置选择。

uastackd.lib
uabasecppd.lib
uapkicppd.lib
uaclientcppd.lib
crypt32.lib
libcryptod.lib
ws2_32.lib
rpcrt4.lib
xmlparsercppd.lib
libxml2d.lib

For Additional Dependencies (Release) enter:根据配置选择。

uastack.lib
uabasecpp.lib
uapkicpp.lib
uaclientcpp.lib
crypt32.lib
libcrypto.lib
ws2_32.lib
rpcrt4.lib
xmlparsercpp.lib
libxml2.lib

Add Preprocessor Defines,Add:

_UA_STACK_USE_DLL
UNICODE
_UNICODE
_CRT_SECURE_NO_WARNINGS
_CRT_SECURE_NO_DEPRECATE

将…\uasdkcppbundlepubsub-bin-EVAL-windows-vs2015_x64-v1.8.7-644\bin\uastack.dll和…\uasdkcppbundlepubsub-bin-EVAL-windows-vs2015_x64-v1.8.7-644\third-party\win64\vs2022\libxml2\out32dll\libxml2.dll复制到exe路径…\Release

代码

连接

UaStatus SampleClient::connect()
{
    UaStatus result;

    // For now we use a hardcoded URL to connect to the local DemoServer
    UaString sURL("opc.tcp://192.168.1.3:4840");

    // Provide information about the client
    SessionConnectInfo sessionConnectInfo;
    UaString sNodeName("unknown_host");
    char szHostName[256];
    if (0 == UA_GetHostname(szHostName, 256))
    {
        sNodeName = szHostName;
    }
    sessionConnectInfo.sApplicationName = "Unified Automation Getting Started Client";
    // Use the host name to generate a unique application URI
    sessionConnectInfo.sApplicationUri  = UaString("urn:%1:UnifiedAutomation:GettingStartedClient").arg(sNodeName);
    sessionConnectInfo.sProductUri      = "urn:UnifiedAutomation:GettingStartedClient";
    sessionConnectInfo.sSessionName     = sessionConnectInfo.sApplicationUri;

    // Security settings are not initialized - we connect without security for now
    SessionSecurityInfo sessionSecurityInfo;

    printf("\nConnecting to %s\n", sURL.toUtf8());
    result = m_pSession->connect(
        sURL,
        sessionConnectInfo,
        sessionSecurityInfo,
        this);

    if (result.isGood())
    {
        printf("Connect succeeded\n");
    }
    else
    {
        printf("Connect failed with status %s\n", result.toString().toUtf8());
    }

    return result;
}

断开

UaStatus SampleClient::disconnect()
{
    UaStatus result;

    // Default settings like timeout
    ServiceSettings serviceSettings;

    printf("\nDisconnecting ...\n");
    result = m_pSession->disconnect(
        serviceSettings,
        OpcUa_True);

    if (result.isGood())
    {
        printf("Disconnect succeeded\n");
    }
    else
    {
        printf("Disconnect failed with status %s\n", result.toString().toUtf8());
    }

    return result;
}

读取变量

UaStatus SampleClient::read()
{
    UaStatus          result;
    ServiceSettings   serviceSettings;
    UaReadValueIds    nodeToRead;
    UaDataValues      values;
    UaDiagnosticInfos diagnosticInfos;

    // Configure one node to read
    // We read the value of the ServerStatus -> CurrentTime

    nodeToRead.create(1);
    
    UaNodeId volt(UaString("|var|Leadshine-ARM-Linux-SM-CNC.Application.PLC_PRG.test"), 4);
    OpcUa_NodeId_CopyTo(volt, &nodeToRead[0].NodeId);
    nodeToRead[0].AttributeId = OpcUa_Attributes_Value;

    printf("\nReading ...\n");
    result = m_pSession->read(
        serviceSettings,
        0,
        OpcUa_TimestampsToReturn_Both,
        nodeToRead,
        values,
        diagnosticInfos);

    if (result.isGood())
    {
        // Read service succeeded - check status of read value
        if (OpcUa_IsGood(values[0].StatusCode))
        {
            printf("ServerStatusCurrentValue: %s\n", UaVariant(values[0].Value).toString().toUtf8());
         }
        else
        {
            printf("Read failed for item[0] with status %s\n", UaStatus(values[0].StatusCode).toString().toUtf8());
        }
    }
    else
    {
        // Service call failed
        printf("Read failed with status %s\n", result.toString().toUtf8());
    }

    return result;
}

创建订阅

UaStatus SampleSubscription::createSubscription(UaSession* pSession)
{
    if ( m_pSubscription )
    {
        printf("\nError: Subscription already created\n");
        return OpcUa_BadInvalidState;
    }

    m_pSession = pSession;

    UaStatus result;

    ServiceSettings serviceSettings;
    SubscriptionSettings subscriptionSettings;
    subscriptionSettings.publishingInterval = 100;

    printf("\nCreating subscription ...\n");
    result = pSession->createSubscription(
        serviceSettings,
        this,
        1,
        subscriptionSettings,
        OpcUa_True,
        &m_pSubscription);

    if (result.isGood())
    {
        printf("CreateSubscription succeeded\n");
    }
    else
    {
        m_pSubscription = NULL;
        printf("CreateSubscription failed with status %s\n", result.toString().toUtf8());
    }

    return result;
}

创建数据监测

UaStatus SampleSubscription::createMonitoredItems()
{
    if ( m_pSubscription == NULL )
    {
        printf("\nError: No Subscription created\n");
        return OpcUa_BadInvalidState;
    }

    UaStatus result;
    OpcUa_UInt32 i;
    ServiceSettings serviceSettings;
    UaMonitoredItemCreateRequests itemsToCreate;
    UaMonitoredItemCreateResults createResults;

    // Configure one item to add to subscription
    // We monitor the value of the ServerStatus -> CurrentTime
 
    itemsToCreate.create(1);
    UaNodeId myVar(UaString("|var|Leadshine-ARM-Linux-SM-CNC.Application.PLC_PRG.test"),4);
    myVar.copyTo(&itemsToCreate[0].ItemToMonitor.NodeId);
    itemsToCreate[0].ItemToMonitor.AttributeId          = OpcUa_Attributes_Value;
    itemsToCreate[0].RequestedParameters.ClientHandle   = 1;
    itemsToCreate[0].RequestedParameters.SamplingInterval = 100;
    itemsToCreate[0].RequestedParameters.QueueSize      = 1;
    itemsToCreate[0].RequestedParameters.DiscardOldest  = OpcUa_True;
    itemsToCreate[0].MonitoringMode                     = OpcUa_MonitoringMode_Reporting;


    printf("\nAdding monitored items to subscription ...\n");
    result = m_pSubscription->createMonitoredItems(
        serviceSettings,
        OpcUa_TimestampsToReturn_Both,
        itemsToCreate,
        createResults);

    if (result.isGood())
    {
        // check individual results
        for (i = 0; i < createResults.length(); i++)
        {
            if (OpcUa_IsGood(createResults[i].StatusCode))
            {
                printf("CreateMonitoredItems succeeded for item: %s\n",
                    UaNodeId(itemsToCreate[i].ItemToMonitor.NodeId).toXmlString().toUtf8());
            }
            else
            {
                printf("CreateMonitoredItems failed for item: %s - Status %s\n",
                    UaNodeId(itemsToCreate[i].ItemToMonitor.NodeId).toXmlString().toUtf8(),
                    UaStatus(createResults[i].StatusCode).toString().toUtf8());
            }
        }
    }
    // service call failed
    else
    {
        printf("CreateMonitoredItems failed with status %s\n", result.toString().toUtf8());
    }

    return result;
}

写入变量

UaStatus SampleClient::write()
{
    UaStatus            result;
    ServiceSettings     serviceSettings;
    UaWriteValues       nodesToWrite;
    UaStatusCodeArray   results;
    UaDiagnosticInfos   diagnosticInfos;

    nodesToWrite.create(1);      

    UaNodeId targetId("|var|Leadshine-ARM-Linux-SM-CNC.Application.PLC_PRG.test", 4);
    targetId.copyTo(&nodesToWrite[0].NodeId); 
    nodesToWrite[0].AttributeId = OpcUa_Attributes_Value;

    UaVariant value;                 // 例如变量在 PLC 为 REAL,则 setDouble / setFloat
    value.setBoolean(OpcUa_False);
    OpcUa_Variant_CopyTo(value, &nodesToWrite[0].Value.Value);

    printf("\nWriting...\n");
    result = m_pSession->write(serviceSettings,
        nodesToWrite,
        results,
        diagnosticInfos);

    if (result.isGood() && results[0] == OpcUa_Good)
    {
        printf("Write succeeded.\n");
    }
    else
    {
        printf("Write failed for item with status %s\n", UaStatus(results[0]).toString().toUtf8());
    }
    return result;
}

参考

C++ Based OPC UA Client/Server/PubSub SDK
Read Custom Node from OPCUA with C++ SDK - String as Node identifier

<think>好的,用户想要一个完整的C#上位机项目,涉及视觉和运动控制。首先,我需要理解用户的具体需求。上位机通常用于工业自动化,所以可能需要图像处理、设备通信、运动控制等功能。用户可能有一定的编程基础,但可能对如何整合这些模块不太熟悉。 接下来,我得考虑项目的主要组成部分。视觉部分可能需要使用相机SDK,比如海康或Basler的,或者用开源库如AForge.NET或EmguCV。运动控制方面,常用的有Modbus TCP、EtherCAT,或者控制卡如固高、雷赛的API。通信部分可能需要串口、网口或者OPC UA。 然后,结构设计方面,分层架构比较合适,比如UI层、业务逻辑层和设备驱动层。这样便于维护和扩展。用户可能希望有一个清晰的界面,实时显示图像和控制状态,所以需要设计好UI布局。 代码示例部分,要提供关键模块的示例,比如相机初始化、图像处理、运动控制命令发送。同时,注意事项也要提到,比如线程安全、异常处理、性能优化,这些是实际开发中容易遇到的问题。 用户可能没有提到的深层需求包括系统的稳定性、实时性,以及如何调试和测试整个系统。可能需要推荐一些第三方库和工具,比如HALCON或OpenCV,还有日志工具如NLog。 最后,确保回答结构清晰,分步骤说明,让用户能够逐步实现项目。同时,提醒用户根据具体硬件调整代码,因为不同设备的API可能不同。如果有时间,还可以建议扩展功能,比如数据存储、报警系统等,帮助用户进一步开发。</think>好的!我将从项目组成、技术选型、架构设计、代码示例等方面为您梳理一个C#视觉运控上位机项目的实现框架: # 一、项目组成模块 1. **视觉处理模块** - 工业相机SDK集成(海康/大华/Basler) - 图像采集显示 - 图像处理算法(定位/测量/OCR) 2. **运动控制模块** - PLC通信协议(Modbus TCP/EtherCAT) - 运动控制卡API集成(固高/雷赛) - 轴参数配置状态监控 3. **人机交互模块** - 工艺流程配置界面 - 实时数据可视化 - 报警日志系统 # 二、技术选型建议 ```csharp // 典型技术组合示例 Vision: AForge.NET/OpenCvSharp/EmguCV + Halcon Motion: OPC UA + Modbus库(NModbus) UI: WPF + MVVM模式 + OxyPlot图表 Communication: Socket/SerialPort/PLC驱动库 ``` # 三、核心架构设计 ``` 1. 分层架构: - UI层(WPF/XAML) - 业务逻辑层(视觉算法/运动规划) - 设备驱动层(相机/PLC/运动卡) 2. 通信架构: 采用生产者-消费者模式,通过队列实现: 图像采集线程 -> 图像处理线程 -> 控制指令生成线程 ``` # 四、关键代码示例 1. **相机初始化(以海康SDK为例)** ```csharp public void InitCamera() { int iDeviceNum = 0; CHCNetSDK.NET_DVR_Init(); CHCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V30(); int lUserID = CHCNetSDK.NET_DVR_Login_V30("192.168.1.64", 8000, "admin", "12345", ref deviceInfo); } ``` 2. **运动控制指令发送** ```csharp public void SendMotionCommand(int axis, double position) { // 固高运动控制卡示例 gts.mc.GT_SetPos(1, (short)axis, (int)(position * 1000)); gts.mc.GT_Update(1, 1 << (axis - 1)); } ``` 3. **图像处理流程** ```csharp using Emgu.CV; using Emgu.CV.Structure; public Mat ProcessImage(Mat src) { // 灰度化 + 边缘检测 Mat gray = new Mat(); CvInvoke.CvtColor(src, gray, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray); Mat edges = new Mat(); CvInvoke.Canny(gray, edges, 100, 200); return edges; } ``` # 五、项目注意事项 1. **线程安全** - 使用Invoke进行UI线程更新 - 图像处理采用双缓存队列 2. **异常处理** ```csharp try { // 设备操作代码 } catch (MotionException ex) { Logger.Error($"运动控制异常: {ex.ErrorCode}"); EmergencyStop(); } ``` 3. **性能优化** - 使用内存映射处理大图像数据 - 对运动指令进行插补算法优化 - 采用硬件加速(GPU/CUDA) # 六、典型应用场景 1. 视觉定位 + 运动跟随系统 2. 尺寸测量 + 分拣控制系统 3. 二维码识别 + 物流分拣系统 建议开发步骤: 1. 先建立基础通信框架 2. 实现单相机单轴基础功能 3. 逐步扩展多相机多轴系统 4. 最后集成视觉算法和运动规划 需要根据具体硬件选型调整驱动接口,建议使用: - 工业相机:Basler ace系列/海康MV-CA系列 - 运动控制:固高GTS系列/欧姆龙NJ系列 - 通信协议:EtherCAT(实时性要求高时)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值