| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- package com.mes.test;
- import com.mes.util.OpcUaUtils;
- import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
- import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
- import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
- import java.util.Scanner;
- /**
- * 刀具寿命采集测试类
- * 用于测试从西门子CNC系统采集刀具寿命数据
- *
- * 采集参数:
- * - $TC_MOP4[T,D] - 剩余工件数
- * - $TC_MOP3[T,D] - 预警阈值
- * - $TC_MOP13[T,D] - 目标寿命值
- */
- public class ToolLifeTest {
-
- private OpcUaUtils opcUaClient;
-
- public ToolLifeTest(String endpointUrl, String username, String password) {
- this.opcUaClient = new OpcUaUtils(endpointUrl, username, password);
- }
-
- /**
- * 连接到OPC UA服务器
- */
- public boolean connect() {
- System.out.println("正在连接到CNC设备...");
- boolean connected = opcUaClient.connect();
- if (connected) {
- System.out.println("✓ 连接成功");
- } else {
- System.out.println("✗ 连接失败");
- }
- return connected;
- }
-
- /**
- * 读取刀具寿命数据
- * @param dNumber D号(刀具号)
- * @param hNumber H号(刀沿号)
- */
- public void readToolLifeData(int dNumber, int hNumber) {
- System.out.println("\n========================================");
- System.out.println("刀具信息: D" + dNumber + " H" + hNumber);
- System.out.println("========================================");
-
- try {
- // 读取剩余工件数 $TC_MOP4[D,H]
- Integer remainingLife = readToolParameter("TC_MOP4", dNumber, hNumber);
-
- // 读取预警阈值 $TC_MOP3[D,H]
- Integer warningThreshold = readToolParameter("TC_MOP3", dNumber, hNumber);
-
- // 读取目标寿命值 $TC_MOP13[D,H]
- Integer targetLife = readToolParameter("TC_MOP13", dNumber, hNumber);
-
- // 显示结果
- System.out.println("\n采集结果:");
- System.out.println("----------------------------------------");
- System.out.println("剩余工件数 ($TC_MOP4): " + (remainingLife != null ? remainingLife : "读取失败"));
- System.out.println("预警阈值 ($TC_MOP3): " + (warningThreshold != null ? warningThreshold : "读取失败"));
- System.out.println("目标寿命 ($TC_MOP13): " + (targetLife != null ? targetLife : "读取失败"));
-
- // 计算寿命百分比和状态
- if (remainingLife != null && targetLife != null && targetLife > 0) {
- double lifePercentage = (remainingLife * 100.0) / targetLife;
- System.out.println("寿命百分比: " + String.format("%.2f%%", lifePercentage));
-
- // 判断状态
- String status;
- if (remainingLife == 0) {
- status = "❌ 寿命耗尽";
- } else if (warningThreshold != null && remainingLife <= warningThreshold) {
- status = "⚠️ 预警状态";
- } else {
- status = "✓ 正常";
- }
- System.out.println("刀具状态: " + status);
- }
- System.out.println("----------------------------------------\n");
-
- } catch (Exception e) {
- System.err.println("读取刀具数据时发生错误: " + e.getMessage());
- e.printStackTrace();
- }
- }
-
- /**
- * 读取刀具参数
- * @param paramName 参数名称 (TC_MOP3, TC_MOP4, TC_MOP13)
- * @param dNumber D号(刀具号)
- * @param hNumber H号(刀沿号)- 暂时未使用
- * @return 参数值
- */
- private Integer readToolParameter(String paramName, int dNumber, int hNumber) {
- // 根据测试结果,索引映射关系:
- // $TC_MOP4[T,D] -> /Tool/Supervision/data[u1,cT,4] (剩余工件数)
- // $TC_MOP3[T,D] -> /Tool/Supervision/data[u1,cT,3] (预警阈值)
- // $TC_MOP13[T,D] -> /Tool/Supervision/data[u1,cT,6] (目标寿命)
-
- int index;
- if (paramName.equals("TC_MOP4")) {
- index = 4; // 剩余工件数
- } else if (paramName.equals("TC_MOP3")) {
- index = 3; // 预警阈值
- } else if (paramName.equals("TC_MOP13")) {
- index = 6; // 目标寿命
- } else {
- System.out.println(" ⚠️ 未知参数: " + paramName);
- return null;
- }
-
- String path = "/Tool/Supervision/data[u1,c" + dNumber + "," + index + "]";
-
- try {
- System.out.println("读取路径: " + path);
- DataValue dataValue = opcUaClient.readValue(path);
-
- if (dataValue != null && dataValue.getValue() != null && dataValue.getValue().getValue() != null) {
- Object value = dataValue.getValue().getValue();
- if (value instanceof Number) {
- Integer result = ((Number) value).intValue();
- System.out.println(" ✓ 读取成功: " + result);
- return result;
- }
- }
- } catch (Exception e) {
- System.out.println(" ✗ 读取失败: " + e.getMessage());
- }
-
- return null;
- }
-
- /**
- * 测试读取Tool/Supervision/data的不同索引
- */
- public void testToolSupervisionData(int dNumber) {
- System.out.println("\n========================================");
- System.out.println("测试 /Tool/Supervision/data 不同索引 (D" + dNumber + ")");
- System.out.println("========================================\n");
-
- // 测试索引 1-20
- for (int i = 1; i <= 20; i++) {
- String path = "/Tool/Supervision/data[u1,c" + dNumber + "," + i + "]";
- try {
- DataValue dataValue = opcUaClient.readValue(path);
- if (dataValue != null && dataValue.getValue() != null && dataValue.getValue().getValue() != null) {
- Object value = dataValue.getValue().getValue();
- System.out.println("索引 " + i + ": " + value);
- }
- } catch (Exception e) {
- // 忽略错误
- }
- }
- }
-
- /**
- * 查找刀具寿命三个参数的索引映射
- * 目标:找到哪个索引对应 $TC_MOP4、$TC_MOP3、$TC_MOP13
- */
- public void findToolLifeIndexes(int dNumber) {
- System.out.println("\n========================================");
- System.out.println("查找刀具寿命参数索引映射 (D" + dNumber + ")");
- System.out.println("========================================");
- System.out.println("目标值:");
- System.out.println(" $TC_MOP4[" + dNumber + ",1] - 剩余工件数(期望:2300左右)");
- System.out.println(" $TC_MOP3[" + dNumber + ",1] - 预警阈值(期望:20左右)");
- System.out.println(" $TC_MOP13[" + dNumber + ",1] - 目标寿命(期望:2280左右)");
- System.out.println("\n开始扫描索引 1-30...\n");
-
- // 扫描索引 1-30
- for (int i = 1; i <= 30; i++) {
- String path = "/Tool/Supervision/data[u1,c" + dNumber + "," + i + "]";
- try {
- DataValue dataValue = opcUaClient.readValue(path);
- if (dataValue != null && dataValue.getValue() != null && dataValue.getValue().getValue() != null) {
- Object value = dataValue.getValue().getValue();
-
- // 转换为数字
- if (value instanceof Number) {
- int intValue = ((Number) value).intValue();
-
- // 判断可能对应的参数
- String guess = "";
- if (intValue > 1000 && intValue < 3000) {
- guess = " <- 可能是 $TC_MOP4 (剩余工件数) 或 $TC_MOP13 (目标寿命)";
- } else if (intValue > 10 && intValue < 100) {
- guess = " <- 可能是 $TC_MOP3 (预警阈值)";
- } else if (intValue == 0) {
- continue; // 跳过0值
- }
-
- System.out.println("索引 " + String.format("%2d", i) + ": " + String.format("%6d", intValue) + guess);
- }
- }
- } catch (Exception e) {
- // 忽略错误
- }
- }
-
- System.out.println("\n========================================");
- System.out.println("扫描完成!请根据上面的值判断索引映射关系");
- System.out.println("========================================");
- }
-
- /**
- * 浏览OPC UA节点树,查找刀具相关节点
- */
- public void browseToolNodes() {
- System.out.println("\n========================================");
- System.out.println("浏览OPC UA节点结构");
- System.out.println("========================================\n");
-
- String[] basePaths = {
- "/Tool",
- "/Nck",
- "/Channel",
- "/Tool/Supervision",
- "/Tool/Data"
- };
-
- for (String basePath : basePaths) {
- System.out.println("\n检查路径: " + basePath);
- try {
- // 尝试读取该路径
- DataValue dataValue = opcUaClient.readValue(basePath);
- if (dataValue != null) {
- System.out.println(" ✓ 路径存在");
- }
- } catch (Exception e) {
- System.out.println(" ✗ 路径不存在或无法访问: " + e.getMessage());
- }
- }
- }
-
- /**
- * 批量读取多个刀具的寿命数据
- */
- public void readMultipleTools(int[] dNumbers, int[] hNumbers) {
- if (dNumbers.length != hNumbers.length) {
- System.err.println("D号和H号数量不匹配");
- return;
- }
-
- for (int i = 0; i < dNumbers.length; i++) {
- readToolLifeData(dNumbers[i], hNumbers[i]);
-
- // 间隔一下,避免请求过快
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * 测试自定义路径(支持指定命名空间)
- */
- public void testCustomPath(String path) {
- System.out.println("\n测试路径: " + path);
-
- // 尝试不同的命名空间索引 (ns=2, ns=3, ns=4)
- int[] namespaces = {2, 3, 4, 5};
-
- for (int ns : namespaces) {
- try {
- System.out.println("尝试命名空间 ns=" + ns);
- NodeId nodeId = new NodeId(ns, path);
- DataValue dataValue = opcUaClient.getClient().readValue(0.0, TimestampsToReturn.Both, nodeId).get();
-
- if (dataValue != null && dataValue.getValue() != null && dataValue.getValue().getValue() != null) {
- Object value = dataValue.getValue().getValue();
- System.out.println("✓ 读取成功 (ns=" + ns + ")");
- System.out.println(" 值: " + value);
- System.out.println(" 类型: " + value.getClass().getSimpleName());
- return;
- } else {
- System.out.println(" 状态: " + (dataValue != null ? dataValue.getStatusCode() : "null"));
- }
- } catch (Exception e) {
- System.out.println(" 失败: " + e.getMessage());
- }
- }
-
- System.out.println("✗ 所有命名空间都读取失败");
- }
-
- /**
- * 获取OPC UA客户端实例(用于测试)
- */
- public OpcUaUtils getClient() {
- return opcUaClient;
- }
-
- /**
- * 批量测试多个刀具的寿命数据(1-8号和20号刀)
- */
- public void testMultipleToolsLife() {
- System.out.println("\n========================================");
- System.out.println("批量测试刀具寿命(1-8号和20号刀)");
- System.out.println("========================================\n");
-
- // 测试刀具号:1, 2, 3, 4, 5, 6, 7, 8, 20
- int[] toolNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 20};
-
- for (int toolNo : toolNumbers) {
- System.out.println("\n----------------------------------------");
- System.out.println("刀具 T" + toolNo + "D1 的寿命数据:");
- System.out.println("----------------------------------------");
-
- try {
- // 读取三个参数
- Integer mop4 = readToolLifeParameter(toolNo, 4); // 剩余工件数
- Integer mop3 = readToolLifeParameter(toolNo, 3); // 预警阈值
- Integer mop13 = readToolLifeParameter(toolNo, 6); // 目标寿命
-
- // 显示结果
- System.out.println("剩余工件数 ($TC_MOP4): " + (mop4 != null ? mop4 : "读取失败"));
- System.out.println("预警阈值 ($TC_MOP3): " + (mop3 != null ? mop3 : "读取失败"));
- System.out.println("目标寿命 ($TC_MOP13): " + (mop13 != null ? mop13 : "读取失败"));
-
- // 计算寿命百分比和状态
- if (mop4 != null && mop13 != null && mop13 > 0) {
- double lifePercentage = (mop4 * 100.0) / mop13;
- System.out.println("寿命百分比: " + String.format("%.2f%%", lifePercentage));
-
- // 判断状态
- String status;
- if (mop4 == 0) {
- status = "❌ 寿命耗尽";
- } else if (mop3 != null && mop4 <= mop3) {
- status = "⚠️ 预警状态";
- } else {
- status = "✓ 正常";
- }
- System.out.println("刀具状态: " + status);
- }
-
- // 间隔一下
- Thread.sleep(300);
-
- } catch (Exception e) {
- System.err.println("读取刀具 T" + toolNo + " 数据时发生错误: " + e.getMessage());
- }
- }
-
- System.out.println("\n========================================");
- System.out.println("批量测试完成!");
- System.out.println("========================================");
- }
-
- /**
- * 读取刀具寿命参数(简化版,不打印详细日志)
- */
- private Integer readToolLifeParameter(int toolNo, int index) {
- String path = "/Tool/Supervision/data[u1,c" + toolNo + "," + index + "]";
-
- try {
- DataValue dataValue = opcUaClient.readValue(path);
-
- if (dataValue != null && dataValue.getValue() != null && dataValue.getValue().getValue() != null) {
- Object value = dataValue.getValue().getValue();
- if (value instanceof Number) {
- return ((Number) value).intValue();
- }
- }
- } catch (Exception e) {
- // 忽略错误
- }
-
- return null;
- }
-
- /**
- * 断开连接
- */
- public void disconnect() {
- if (opcUaClient != null) {
- opcUaClient.disconnect();
- System.out.println("已断开连接");
- }
- }
-
- /**
- * 主测试方法
- */
- public static void main(String[] args) {
- Scanner scanner = new Scanner(System.in);
-
- // 配置连接参数
- System.out.println("=== CNC刀具寿命采集测试 ===\n");
-
- System.out.print("请输入CNC设备IP地址 (默认: 80.23.10.45): ");
- String ip = scanner.nextLine().trim();
- if (ip.isEmpty()) {
- ip = "80.23.10.45";
- }
-
- String endpointUrl = "opc.tcp://" + ip + ":4840";
- String username = "OPCUA";
- String password = "OPCUA123";
-
- ToolLifeTest test = new ToolLifeTest(endpointUrl, username, password);
-
- // 连接到设备
- if (!test.connect()) {
- System.err.println("无法连接到设备,程序退出");
- scanner.close();
- return;
- }
-
- // 交互式测试
- while (true) {
- System.out.println("\n请选择操作:");
- System.out.println("1. 读取单个刀具寿命");
- System.out.println("2. 批量读取刀具寿命");
- System.out.println("3. 浏览OPC UA节点结构");
- System.out.println("4. 测试已知路径");
- System.out.println("5. 测试Tool/Supervision/data索引");
- System.out.println("6. 测试刀具寿命三个参数(找索引映射)");
- System.out.println("7. 批量测试多个刀具(1-8和20号刀)");
- System.out.println("8. 退出");
- System.out.print("请输入选项 (1-8): ");
-
- String choice = scanner.nextLine().trim();
-
- switch (choice) {
- case "1":
- // 单个刀具测试
- System.out.print("请输入D号 (刀具号,例如: 1): ");
- int dNumber = Integer.parseInt(scanner.nextLine().trim());
-
- System.out.print("请输入H号 (刀沿号,例如: 1): ");
- int hNumber = Integer.parseInt(scanner.nextLine().trim());
-
- test.readToolLifeData(dNumber, hNumber);
- break;
-
- case "2":
- // 批量测试 - 根据图片中的刀具表
- System.out.println("\n批量读取刀具寿命 (D1-D3, 各H1刀沿)...");
- int[] dNumbers = {1, 2, 3};
- int[] hNumbers = {1, 1, 1};
- test.readMultipleTools(dNumbers, hNumbers);
- break;
-
- case "3":
- // 浏览节点
- test.browseToolNodes();
- break;
-
- case "4":
- // 测试自定义路径
- System.out.print("请输入完整的OPC UA路径 (例如: /Tool/Supervision/data[u1,c1,3]): ");
- String customPath = scanner.nextLine().trim();
- test.testCustomPath(customPath);
- break;
-
- case "5":
- // 测试Tool/Supervision/data索引
- System.out.print("请输入D号 (刀具号): ");
- int testD = Integer.parseInt(scanner.nextLine().trim());
- test.testToolSupervisionData(testD);
- break;
-
- case "6":
- // 测试三个参数的索引映射
- System.out.print("请输入D号 (刀具号,例如: 3): ");
- int toolD = Integer.parseInt(scanner.nextLine().trim());
- test.findToolLifeIndexes(toolD);
- break;
-
- case "7":
- // 批量测试1-8和20号刀
- test.testMultipleToolsLife();
- break;
-
- case "8":
- // 退出
- test.disconnect();
- scanner.close();
- System.out.println("程序已退出");
- return;
-
- default:
- System.out.println("无效选项,请重新输入");
- }
- }
- }
- }
|