java通过org.eclipse.milo实现OPCUA客户端进行连接和订阅

news2024/12/24 8:49:42

前言

之前写过一篇关于MQTT的方式进行物理访问的文章:SpringBoot集成MQTT,WebSocket返回前端信息_springboot mqtt websocket-CSDN博客

最近又接触到OPCUA协议,想通过java试试看能不能实现。

软件

在使用java实现之前,想着有没有什么模拟器作为服务器端能够进行发送opcua数据,网上搜到好多都是使用KEPServerEX6,下载了之后,发现学习成本好大,这个软件都不会玩,最后终于找到了Prosys OPC UA Simulation Server,相对来说,这个软件的学习成本很低。但是也有一个弊端,只能进行本地模拟。

下载地址:Prosys OPC - OPC UA Simulation Server Downloads

下载安装完成之后,打开页面就可以看到,软件生成的opcua测试地址

为了方便操作,把所有的菜单全部暴露出来,点击Options下的Switch to Basic Mode

如果需要修改这个默认的连接地址,可通过 Endpoints 菜单进行设置(我这里用的是默认的地址)。也可以在这个菜单下修改连接方式和加密方式。

也可以在Users下添加用户名和密码

Objects上自带了一些函数能够帮助我们快速进行测试,也可以自己创建(我使用的是自带的)

接下来就是代码

代码

引入依赖

        <dependency>
            <groupId>org.eclipse.milo</groupId>
            <artifactId>sdk-client</artifactId>
            <version>0.6.9</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.70</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.milo</groupId>
            <artifactId>sdk-server</artifactId>
            <version>0.6.9</version>
        </dependency>

目前实现了两种方式:匿名方式、用户名加证书方式,还有仅用户名方式后续继续研究

匿名方式:

import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider;
import org.eclipse.milo.opcua.sdk.server.Session;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.*;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
import org.eclipse.milo.opcua.stack.core.types.structured.UserNameIdentityToken;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;


/**
 * 无密码无证书无安全认证模式
 * @Author: majinzhong
 * @Data:2024/8/30
 */
public class OpcUaTest {
    //opc ua服务端地址
    private final static String endPointUrl = "opc.tcp://Administrator:53530/OPCUA/SimulationServer";
//    private final static String endPointUrl = "opc.tcp://192.168.24.13:4840";

    public static void main(String[] args) {
        try {
            //创建OPC UA客户端
            OpcUaClient opcUaClient = createClient();

            //开启连接
            opcUaClient.connect().get();
            // 订阅消息
            subscribe(opcUaClient);

            // 写入
//            writeValue(opcUaClient);

            // 读取
//            readValue(opcUaClient);

            // 关闭连接
            opcUaClient.disconnect().get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建OPC UA客户端
     *
     * @return
     * @throws Exception
     */
    private static OpcUaClient createClient() throws Exception {
        Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            throw new Exception("unable to create security dir: " + securityTempDir);
        }
            return OpcUaClient.create(endPointUrl,
                endpoints ->
                        endpoints.stream()
                                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                                .findFirst(),
                configBuilder ->
                        configBuilder
                                .setApplicationName(LocalizedText.english("OPC UA test")) // huazh-01
                                .setApplicationUri("urn:eclipse:milo:client") // ns=2:s=huazh-01.device1.data-huazh
                                //访问方式 new AnonymousProvider()
                                .setIdentityProvider(new AnonymousProvider())
                                .setRequestTimeout(UInteger.valueOf(5000))
                                .build()
        );
    }

    private static void subscribe(OpcUaClient client) throws Exception {
        //创建发布间隔1000ms的订阅对象
        client.getSubscriptionManager()
                .createSubscription(1000.0)
                .thenAccept(t -> {
                    //节点ns=2;s=test.device2.test2
//                    NodeId nodeId = new NodeId(4, 322);
                    NodeId nodeId = new NodeId(3, 1003);
                    ReadValueId readValueId = new ReadValueId(nodeId, AttributeId.Value.uid(), null, null);
                    //创建监控的参数
                    MonitoringParameters parameters = new MonitoringParameters(UInteger.valueOf(1), 1000.0, null, UInteger.valueOf(10), true);
                    //创建监控项请求
                    //该请求最后用于创建订阅。
                    MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(readValueId, MonitoringMode.Reporting, parameters);
                    List<MonitoredItemCreateRequest> requests = new ArrayList<>();
                    requests.add(request);
                    //创建监控项,并且注册变量值改变时候的回调函数。
                    t.createMonitoredItems(
                            TimestampsToReturn.Both,
                            requests,
                            (item, id) -> item.setValueConsumer((it, val) -> {
                                System.out.println("=====订阅nodeid====== :" + it.getReadValueId().getNodeId());
                                System.out.println("=====订阅value===== :" + val.getValue().getValue());
                            })
                    );
                }).get();

        //持续订阅
        Thread.sleep(Long.MAX_VALUE);
    }

    public static void readValue(OpcUaClient client) {
        try {
            NodeId nodeId = new NodeId(3, 1002);

            DataValue value = client.readValue(0.0, TimestampsToReturn.Both, nodeId).get();

            System.out.println("=====读取ua1====:" + value.getValue().getValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void writeValue(OpcUaClient client) {
        try {

            //创建变量节点 test.device2.test2
            NodeId nodeId = new NodeId(2, "test.device2.test2");

            //uda3 boolean
            Short value = 11;
            //创建Variant对象和DataValue对象
            Variant v = new Variant(value);
            DataValue dataValue = new DataValue(v, null, null);

            StatusCode statusCode = client.writeValue(nodeId, dataValue).get();
            System.out.println(statusCode);
            System.out.println("=====写入ua1====:" + statusCode.isGood());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

用户名加正式认证方式:

import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.*;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;


/**
 * 有密码有证书有安全认证模式
 * @Author: majinzhong
 * @Data:2024/8/30
 */
public class OpcUaTest2 {
    //opc ua服务端地址
    private final static String endPointUrl = "opc.tcp://Administrator:53530/OPCUA/SimulationServer";
//    private final static String endPointUrl = "opc.tcp://192.168.24.13:4840";

    public static void main(String[] args) {
        try {
            //创建OPC UA客户端
            OpcUaClient opcUaClient = createClient();

            //开启连接
            opcUaClient.connect().get();
            // 订阅消息
            subscribe(opcUaClient);

            // 写入
//            writeValue(opcUaClient);

            // 读取
//            readValue(opcUaClient);

            // 关闭连接
            opcUaClient.disconnect().get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建OPC UA客户端
     *
     * @return
     * @throws Exception
     */
    private static OpcUaClient createClient() throws Exception {
        Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            throw new Exception("unable to create security dir: " + securityTempDir);
        }

        KeyStoreLoader loader = new KeyStoreLoader().load(securityTempDir);

        return OpcUaClient.create(endPointUrl,
                endpoints ->
                        endpoints.stream()
                                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.Basic256Sha256.getUri()))
//                                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                                .findFirst(),
                configBuilder ->
                        configBuilder
                                .setApplicationName(LocalizedText.english("OPC UA test")) // huazh-01
                                .setApplicationUri("urn:eclipse:milo:client") // ns=2:s=huazh-01.device1.data-huazh
                                //访问方式 new AnonymousProvider()
                                .setCertificate(loader.getClientCertificate())
                                .setKeyPair(loader.getClientKeyPair())
                                .setIdentityProvider(new UsernameProvider("TOPNC", "TOPNC123"))
                                .setRequestTimeout(UInteger.valueOf(5000))
                                .build()
        );
    }

    private static void subscribe(OpcUaClient client) throws Exception {
        //创建发布间隔1000ms的订阅对象
        client.getSubscriptionManager()
                .createSubscription(1000.0)
                .thenAccept(t -> {
                    //节点ns=2;s=test.device2.test2
//                    NodeId nodeId = new NodeId(3, "unit/Peri_I_O.gs_ComToRM.r32_A1_Axis_ActValue");
                    NodeId nodeId = new NodeId(3, 1003);
                    ReadValueId readValueId = new ReadValueId(nodeId, AttributeId.Value.uid(), null, null);
                    //创建监控的参数
                    MonitoringParameters parameters = new MonitoringParameters(UInteger.valueOf(1), 1000.0, null, UInteger.valueOf(10), true);
                    //创建监控项请求
                    //该请求最后用于创建订阅。
                    MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(readValueId, MonitoringMode.Reporting, parameters);
                    List<MonitoredItemCreateRequest> requests = new ArrayList<>();
                    requests.add(request);
                    //创建监控项,并且注册变量值改变时候的回调函数。
                    t.createMonitoredItems(
                            TimestampsToReturn.Both,
                            requests,
                            (item, id) -> item.setValueConsumer((it, val) -> {
                                System.out.println("=====订阅nodeid====== :" + it.getReadValueId().getNodeId());
                                System.out.println("=====订阅value===== :" + val.getValue().getValue());
                            })
                    );
                }).get();

        //持续订阅
        Thread.sleep(Long.MAX_VALUE);
    }

    public static void readValue(OpcUaClient client) {
        try {
            NodeId nodeId = new NodeId(3, 1002);

            DataValue value = client.readValue(0.0, TimestampsToReturn.Both, nodeId).get();

            System.out.println("=====读取ua1====:" + value.getValue().getValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void writeValue(OpcUaClient client) {
        try {

            //创建变量节点 test.device2.test2
            NodeId nodeId = new NodeId(2, "test.device2.test2");

            //uda3 boolean
            Short value = 11;
            //创建Variant对象和DataValue对象
            Variant v = new Variant(value);
            DataValue dataValue = new DataValue(v, null, null);

            StatusCode statusCode = client.writeValue(nodeId, dataValue).get();
            System.out.println(statusCode);
            System.out.println("=====写入ua1====:" + statusCode.isGood());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

证书加密类

import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil;
import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder;
import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.regex.Pattern;

/**
 * Created by Cryan on 2021/8/4.
 * TODO.OPCUA  证书生成
 */

class KeyStoreLoader {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static final Pattern IP_ADDR_PATTERN = Pattern.compile(
            "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");

    // 证书别名
    private static final String CLIENT_ALIAS = "client-ai";
    // 获取私钥的密码
    private static final char[] PASSWORD = "password".toCharArray();
    // 证书对象
    private X509Certificate clientCertificate;
    // 密钥对对象
    private KeyPair clientKeyPair;

    KeyStoreLoader load(Path baseDir) throws Exception {
        // 创建一个使用`PKCS12`加密标准的KeyStore。KeyStore在后面将作为读取和生成证书的对象。
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // PKCS12的加密标准的文件后缀是.pfx,其中包含了公钥和私钥。
        // 而其他如.der等的格式只包含公钥,私钥在另外的文件中。
        Path serverKeyStore = baseDir.resolve("example-client.pfx");

        logger.info("Loading KeyStore at {}", serverKeyStore);
        // 如果文件不存在则创建.pfx证书文件。
        if (!Files.exists(serverKeyStore)) {
            keyStore.load(null, PASSWORD);
            // 用2048位的RAS算法。`SelfSignedCertificateGenerator`为Milo库的对象。
            KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048);
            // `SelfSignedCertificateBuilder`也是Milo库的对象,用来生成证书。
            // 中间所设置的证书属性可以自行修改。
            SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair)
                    .setCommonName("Eclipse Milo Example Client test")
                    .setOrganization("mjz")
                    .setOrganizationalUnit("dev")
                    .setLocalityName("mjz")
                    .setStateName("CA")
                    .setCountryCode("US")
                    .setApplicationUri("urn:eclipse:milo:client")
                    .addDnsName("localhost")
                    .addIpAddress("127.0.0.1");

            // Get as many hostnames and IP addresses as we can listed in the certificate.
            for (String hostname : HostnameUtil.getHostnames("0.0.0.0")) {
                if (IP_ADDR_PATTERN.matcher(hostname).matches()) {
                    builder.addIpAddress(hostname);
                } else {
                    builder.addDnsName(hostname);
                }
            }
            // 创建证书
            X509Certificate certificate = builder.build();
            // 设置对应私钥的别名,密码,证书链
            keyStore.setKeyEntry(CLIENT_ALIAS, keyPair.getPrivate(), PASSWORD, new X509Certificate[]{certificate});
            try (OutputStream out = Files.newOutputStream(serverKeyStore)) {
                // 保存证书到输出流
                keyStore.store(out, PASSWORD);
            }
        } else {
            try (InputStream in = Files.newInputStream(serverKeyStore)) {
                // 如果文件存在则读取
                keyStore.load(in, PASSWORD);
            }
        }
        // 用密码获取对应别名的私钥。
        Key serverPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD);
        if (serverPrivateKey instanceof PrivateKey) {
            // 获取对应别名的证书对象。
            clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS);
            // 获取公钥
            PublicKey serverPublicKey = clientCertificate.getPublicKey();
            // 创建Keypair对象。
            clientKeyPair = new KeyPair(serverPublicKey, (PrivateKey) serverPrivateKey);
        }
        return this;
    }
    // 返回证书
    X509Certificate getClientCertificate() {
        return clientCertificate;
    }

    // 返回密钥对
    KeyPair getClientKeyPair() {
        return clientKeyPair;
    }
}

代码讲解

仔细阅读代码不难发现,匿名方式和用户名加正式方式仅仅只有这一块不太一样

配置完成之后,需要修改想要订阅的节点,进行读取数据,匿名方式和用户名加证书方式一致,都是在代码的NodeId nodeId = new NodeId(3, 1003);进行修改,其中的3和1003对应软件上Objects上的

运行

一切配置好并且修改好之后,先运行匿名方式!匿名方式!匿名方式!!!(用户名加证书方式还有一个点,下面再说)

可以看到已经能够读取到节点的数据了

第一次运行用户名加证书方式的时候,会报java.lang.RuntimeException: java.util.concurrent.ExecutionException: UaException: status=Bad_SecurityChecksFailed, message=Bad_SecurityChecksFailed (code=0x80130000, description="An error occurred verifying security.")的错误,这是因为证书没有被添加信任

在Certificates下找到自己的证书,将Reject改成Trust即可。

因为代码中setApplicationUri时写的是urn:eclipse:milo:client,所以这个就是刚刚代码创建的证书。

运行用户名加证书方式

已经可以正常读取到节点数据了

补充

问题一:运行代码时,可能会遇见java.lang.RuntimeException: UaException: status=Bad_ConfigurationError, message=no endpoint selected的错误,这是因为,OPCUA服务器端没有允许这种方式(OPCUA目前我看到的有三种方式:匿名、用户名、用户名加证书),所以需要修改OPCUA服务器端添加这种方式,添加在 Endpoints菜单下,或者查看服务器端支持哪种方式,修改代码。

问题二:org.eclipse.milo.opcua.stack.core.UaException: no KeyPair configured

这种是因为没有配置密钥,代码方面出现了问题,需要在创建客户端的时候setKeyPair()

问题三:org.eclipse.milo.opcua.stack.core.UaException: no certificate configured

这种时因为没有配置证书,代码方面出现了问题,需要在创建客户端的时候setCertificate()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2151604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

欠款管理代码———未来之窗行业应用跨平台架构

一、欠款管理代码 function fun_会员_还款操作(会员卡信息id,MainID){var 未来之窗vos对话框_内容 ";var title"test";var 未来之窗vos对话框_id"hjksgfjkkhkj_child";CyberWin_Dialog.layer(未来之窗vos对话框_内容,{type:"url",title:&…

windows下,用docker部署xinference,为什么老是提示localhost无法访问?

部署xinference有两种方式&#xff1a; 一、本地部署 &#xff08;略&#xff09; 二、使用Docker部署&#xff08;与运行&#xff09; 其中又包括&#xff1a; 1&#xff09;使用CPU的方式&#xff1a;&#xff08;略&#xff09; 1&#xff09;使用GPU的方式&#xff1…

LeetCode_sql_day30(1264.页面推荐)

描述 1264.页面推荐 朋友关系列表&#xff1a; Friendship ------------------------ | Column Name | Type | ------------------------ | user1_id | int | | user2_id | int | ------------------------ (user1_id, user2_id) 是这张表具有唯一值的…

低代码可视化工具--vue条件判断v-if可视化设置-代码生成器

在Vue UniApp中&#xff0c;条件判断通常是通过指令v-if、v-else-if、v-else来实现的。这些机制允许你根据表达式的真假值来决定是否渲染某个元素或元素组&#xff0c;或者执行特定的逻辑。 条件判断说明 v-if 是惰性的&#xff1a;如果在初始渲染时条件为假&#xff0c;则什么…

mac os x 找不到钥匙串访问

昨天手贱更新了最新的mac系统&#xff0c;结果在实用工具中找不到钥匙串访问APP了。。。 最新mac系统为 15.0 (24A335) 真是醉了。。。 那就得想办法把他给呼出来&#xff0c;在开发者中心下载了一个.cer文件&#xff0c;然后双击打开&#xff0c;此时钥匙串打开了&#xff…

猿大师办公助手在线编辑Office为什么要在客户端电脑安装插件微软Office或金山WPS?

猿大师办公助手作为一款专业级的网页编辑Office方案&#xff0c;与在线云文档方案&#xff08;飞书、腾讯文档等&#xff09;不同&#xff0c;需要在客户端电脑安装猿大师办公助手插件及微软Office或者金山WPS软件&#xff0c;很多客户不理解为什么要这么麻烦&#xff0c;能否客…

从虚拟到现实:数字孪生与数字样机的进化之路

数字化技术高速发展的当下&#xff0c;计算机辅助技术已成为产品设计研发中不可或缺的一环&#xff0c;数字样机&#xff08;Digital Prototype, DP&#xff09;与数字孪生技术便是产品研发数字化的典型方法。本文将主要介绍数字样机与数字孪生在国内外的发展&#xff0c;并针对…

机器翻译之创建Seq2Seq的编码器、解码器

1.创建编码器、解码器的基类 1.1创建编码器的基类 from torch import nn#构建编码器的基类 class Encoder(nn.Module): #继承父类nn.Moduledef __init__(self, **kwargs): #**kwargs&#xff1a;不定常的关键字参数super().__init__(**kwargs)def forward(self, X, *args…

[产品管理-29]:NPDP新产品开发 - 27 - 战略 - 分层战略与示例

目录 1. 公司战略 2. 经营战略 3. 创新战略 4. 新产品组合战略 5. 新产品开发战略 战略分层是企业规划和管理的重要组成部分&#xff0c;它涉及不同层级的战略制定和实施。以下是根据您的要求&#xff0c;对公司战略、经营战略、创新战略、新产品组合战略、新产品开发战略…

闯关leetcode——66. Plus One

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/plus-one/description/ 内容 You are given a large integer represented as an integer array digits, where each digits[i] is the ith digit of the integer. The digits are ordered from mo…

2016年国赛高教杯数学建模B题小区开放对道路通行的影响解题全过程文档及程序

2016年国赛高教杯数学建模 B题 小区开放对道路通行的影响 2016年2月21日&#xff0c;国务院发布《关于进一步加强城市规划建设管理工作的若干意见》&#xff0c;其中第十六条关于推广街区制&#xff0c;原则上不再建设封闭住宅小区&#xff0c;已建成的住宅小区和单位大院要逐…

TinkerTool System for Mac实用软件系统维护工具

TinkerTool System 是一款功能全面且强大的 Mac 实用软件&#xff0c;具有以下特点和功能&#xff1a; 软件下载地址 维护功能&#xff1a; 磁盘清理&#xff1a;能够快速扫描并清理系统中的垃圾文件、临时文件以及其他无用文件&#xff0c;释放宝贵的磁盘空间&#xff0c;保…

c++优先级队列自定义排序实现方式

1、使用常规方法实现 使用结构体实现自定义排序函数 2、使用lambda表达式实现 使用lambda表达式实现自定义排序函数 3、具体实现如下&#xff1a; #include <iostream> #include <queue> #include <vector>using namespace std; using Pair pair<in…

FL Studio 24.1.1.4285中文破解完整版免费下载FL 2024注册密钥完整版crack百度云安装包下载

FL Studio 24.1.1.4285中文破解版是一个强大的软件选项&#xff0c;可以使用专业应用程序&#xff08;如最先进的录音机、均衡器、内置工具等&#xff09;制作循环和歌曲。它由数百个合成器和混响等效果以及均衡器组成&#xff0c;除此之外&#xff0c;您还可以在新音乐制作的方…

solidwork找不到曲面

如果找不到曲面 则右键找到选项卡&#xff0c;选择曲面

Kafka3.8.0+Centos7.9的安装参考

Kafka3.8.0Centos7.9的安装参考 环境准备 操作系统版本&#xff1a;centos7.9 用户/密码&#xff1a;root/1qazXSW 主机名 IP地址 安装软件 下载地址 k1 192.168.207.131 jdk1.8zookeeper3.9.2kafka_2.13-3.8.0efak-web-3.0.1 1&#xff09; Java Downloads | Oracle …

双击热备 Electron网页客户端

安装流程&#xff1a; 1.下载node.js安装包进行安装 2.点击Next; 3.勾选&#xff0c;点击Next; 4.选择安装目录 5.选择Online 模式 6.下一步执行安装 。 7.运行cmd,执行命令 path 和 node --version&#xff0c;查看配置路径和版本 8.Goland安装插件node.js 9.配置运行…

Vue 修饰符 | 指令 区别

Vue 修饰符 | 指令 区别 在Vue.js这个前端框架中&#xff0c;修饰符&#xff08;modifiers&#xff09;和指令&#xff08;directives&#xff09;是两个非常重要的概念。这里我们深度讨论下他们区别。 文章目录 Vue 修饰符 | 指令 区别一、什么是修饰符修饰符案例常见修饰符列…

2024年“华为杯”研赛第二十一届中国研究生数学建模竞赛解题思路|完整代码论文集合

我是Tina表姐&#xff0c;毕业于中国人民大学&#xff0c;对数学建模的热爱让我在这一领域深耕多年。我的建模思路已经帮助了百余位学习者和参赛者在数学建模的道路上取得了显著的进步和成就。现在&#xff0c;我将这份宝贵的经验和知识凝练成一份全面的解题思路与代码论文集合…

Elasticsearch:检索增强生成背后的重要思想

作者&#xff1a;来自 Elastic Jessica L. Moszkowicz 星期天晚上 10 点&#xff0c;我九年级的女儿哭着冲进我的房间。她说她对代数一无所知&#xff0c;注定要失败。我进入超级妈妈模式&#xff0c;却发现我一点高中数学知识都不记得了。于是&#xff0c;我做了任何一位超级妈…