当前位置: 首页 > article >正文

opcua认证测试1108 增加对三菱,西门子,modbus支持

一句话概括:在服务器中添加三个独立线程,对三菱,西门子,modbus的服务器进行读取地址位置,并将值转换,更新到服务器上的相应节点上。实现opcua对上述三种协议的支持。而上位机只需专注于对opcua服务器上数据获取即可,不用操心更多的plc问题。

1.线程:单独获取对应plc地址上数据

2.数据转换,并更新至对应节点

主要核心概括:

服务器启动时,读取三菱,西门子,modubus的一个地址位置,更新到节点上

https://www.cnblogs.com/dathlin/p/8976955.html   C# OPC UA服务器 OPC UA网关 三菱 西门子 欧姆龙 Modbus转OPC UA 服务器 可配置的OPC UA服务器网关 HslSharp软件文档

https://blog.csdn.net/qq_38693757/article/details/121675847  c#读取ini

服务器增加三菱,西门子,modbus相关节点。增加对应节点读写能力。测试更新后的三协议通讯,测试ctt

主要实践内容:

C#读写三菱PLC和西门子PLC数据 使用TCP/IP 协议 

C# 读写西门子PLC数据,包含S7协议和Fetch/Write协议,s7支持200smart,300PLC,1200PLC,1500PLC - dathlin - 博客园

C# ModBus Tcp读写数据 与服务器进行通讯 安卓modbus tcp 

举例三菱:单独开线程,去读取对应地址位置。并更新数据

  //线程启动的地方   
//melsec thread
     if (IniFunc.getString("IsUsePlc", "IsUsePlc1", null, filename)=="true")
     {
         Thread melsec_thread = new Thread(MelsecStatusThreadAsync);
         melsec_thread.Start();
         Utils.LogInfo(" melsec_thread.Start");
     }




   /// <summary>
   /// Melsec Status thread, prints  value of that address every 10 seconds.
   /// </summary>
   private void MelsecStatusThreadAsync()
   {
       string a = IniFunc.getString("Server", "TimeInterval", null, filename);
       int timeinterval = Convert.ToInt32(a);
       // 配置PLC连接参数
       string ipAddress = IniFunc.getString("IPPORT1", "IP", null, filename);  // PLC的IP地址
       string port = IniFunc.getString("IPPORT1", "Port", null, filename);   // PLC的IP地址; // PLC的端口号
       _melsecMc_net = new MelsecMcNet();
       _melsecMc_net.IpAddress = ipAddress;
       _melsecMc_net.Port = int.Parse(port);
       _melsecMc_net.ConnectTimeOut = 10000; // 网络连接的超时时间


       try
       {
           OperateResult connect = _melsecMc_net.ConnectServer();
           if (connect.IsSuccess)
           {

               Console.WriteLine("Roll PLC Melsec_MC Connect Success!!");
               Utils.LogInfo("Roll PLC Melsec_MC Connect Success!");

           }
           else
           {

               Console.WriteLine("Roll PLC Melsec_MC Connect Fail!!");
               Utils.LogInfo("Roll PLC Melsec_MC Connect Fail!!");
               return;
           }


           // 开始定期读取使能地址
           while (connect.IsSuccess)
           {
               ReadMelsecAddress();
               // 等待xx秒
               Thread.Sleep(timeinterval);
           }
       }
       catch (Exception ex)
       {
           Console.WriteLine($"发生错误: {ex.Message}");
       }
       finally
       {
           // 断开与PLC的连接
           //if (plc.IsConnected)
           //{
           //    plc.Disconnect();
           //    Console.WriteLine("已断开与PLC的连接。");
           //}
       }
   }
   private void ReadMelsecAddress()
   {
       //melsec
       try
       {
           // update value node
           List<bool> boollist = new List<bool>();
           //1 
           OperateResult<bool> result = new OperateResult<bool>();
           string address = IniFunc.getString("ADDRESS1", "ConnectAdd", null, filename);
           result = _melsecMc_net.ReadBool(address);
           if (result.IsSuccess)
           {
               // 解析并输出读取到的值
               var value = result.Content.ToString(); // 这里可能需要根据实际情况进行值类型转换
               Console.WriteLine($"Melsec地址 {address} 的当前值: {value}");


               boollist.Add(Convert.ToBoolean(value));
               //m_server.CurrentInstance.NodeManager.NodeManagers.Last().ModifyBoolList(boollist);
           }
           else
           {
               Console.WriteLine($"Melsec读取地址 {address} 失败: {result.ErrorCode}");
           }
           //2 LoopEnadbleAdd
           result = new OperateResult<bool>();
           address = IniFunc.getString("ADDRESS1", "LoopEnadbleAdd", null, filename);
           result = _melsecMc_net.ReadBool(address);
           if (result.IsSuccess)
           {
               // 解析并输出读取到的值
               var value = result.Content.ToString(); // 这里可能需要根据实际情况进行值类型转换
               Console.WriteLine($"Melsec地址 {address} 的当前值: {value}");
               // update value node


               boollist.Add(Convert.ToBoolean(value));

           }
           else
           {
               Console.WriteLine($"Melsec读取地址 {address} 失败: {result.ErrorCode}");
           }
           //update
           m_server.CurrentInstance.NodeManager.NodeManagers.Last().ModifyBoolListMelsec(boollist);



           //3 Testint16
           OperateResult<Int16> resultint16 = new OperateResult<Int16>();
           address = IniFunc.getString("ADDRESS1", "Int16Test", null, filename);
           resultint16 = _melsecMc_net.ReadInt16(address);
           if (resultint16.IsSuccess)
           {
               // 解析并输出读取到的值
               var value = resultint16.Content.ToString(); // 这里可能需要根据实际情况进行值类型转换
               Console.WriteLine($"Melsec地址 {address} 的当前值: {value}");
               // update value node
               List<Int16> Int16list = new List<Int16>();

               Int16list.Add(Convert.ToInt16(value));
               m_server.CurrentInstance.NodeManager.NodeManagers.Last().ModifyInt16ListMelsec(Int16list);
           }
           else
           {
               Console.WriteLine($"Melsec读取地址 {address} 失败: {result.ErrorCode}");
           }

       }
       catch (Exception ex)
       {
           Console.WriteLine($"Melsec读取使能地址时发生错误: {ex.Message}");
       }
   }

服务器部分需要增加该节点的创建。增加对节点值的更新方法。

    public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)
{
///增加三菱,西门子,modbus等节点

}

举例三菱:在opcua中增加三菱节点

  #region  Create Melsec
  //1
  FolderState Melsec = CreateFolder(null, "Machines_Melsec");


  Melsec.AddReference(ReferenceTypes.Organizes, true, ObjectIds.ObjectsFolder);
  references.Add(new NodeStateReference(ReferenceTypes.Organizes, false, Melsec.NodeId));
  Melsec.EventNotifier = EventNotifiers.SubscribeToEvents;
  AddRootNotifier(Melsec);
  //2
  string[] machinesMelsec = new string[] { "Melsec" };

  collectTimeListMelsec = new List<BaseDataVariableState<DateTime>>();
  AMSDataListFloatMelsec = new List<BaseDataVariableState<float>>();
  AMSPartitionDataListFloatMelsec = new List<BaseDataVariableState<float>>();
  //增加备用节点Bool
  AMSDataListBoolMelsec = new List<BaseDataVariableState<bool>>();
  AMSDataListStringMelsec = new List<BaseDataVariableState<string>>();
  AMSDataListInt16Melsec = new List<BaseDataVariableState<Int16>>();
  AMSDataListInt32Melsec = new List<BaseDataVariableState<Int32>>();
  AMSDataListInt64Melsec = new List<BaseDataVariableState<Int64>>();
  AMSDataListUInt16Melsec = new List<BaseDataVariableState<UInt16>>();
  AMSDataListUInt32Melsec = new List<BaseDataVariableState<UInt32>>();
  AMSDataListUInt64Melsec = new List<BaseDataVariableState<UInt64>>();

  AMSThicknessMeasDataListFloatMelsec = new List<BaseDataVariableState<float>>();
  AMSDataListDoubleMelsec = new List<BaseDataVariableState<double>>();

  for (int i = 0; i < machinesMelsec.Length; i++)
  {
      FolderState myFolder = CreateFolder(Melsec, machinesMelsec[i]);

      //MachineParam.Closed loop enable    ConnectAdd
      BaseDataVariableState<bool> Closedloopenable = CreateVariable(myFolder, "MachineParam.Closed loop enable", DataTypeIds.Boolean, ValueRanks.Scalar, true);
      Closedloopenable.Description = "Closed loop enable parameter";
      AMSDataListBoolMelsec.Add(Closedloopenable);

      //MachineParam.Closed loop ON/OFF
      BaseDataVariableState<bool> ClosedLoopONOFF = CreateVariable(myFolder, "MachineParam.Closed loop ON/OFF", DataTypeIds.Boolean, ValueRanks.Scalar, true);
      ClosedLoopONOFF.Description = "Closed loop ON/OFF parameter";
      AMSDataListBoolMelsec.Add(ClosedLoopONOFF);

      分区数
      //BaseDataVariableState<float> partitionNumNode = CreateVariable(myFolder, "MachineParam.number.partitions", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //partitionNumNode.Description = "Number of partitions (active lanes)";
      //AMSDataListFloatMelsec.Add(partitionNumNode);

      ProcessVariable.IDProductConfiguration
      //BaseDataVariableState<string> IDProductConfiguration = CreateVariable(myFolder, "ProcessVariable.IDProductConfiguration", DataTypeIds.String, ValueRanks.Scalar, "default");
      //IDProductConfiguration.Description = "ID Production configuration";
      //AMSDataListStringMelsec.Add(IDProductConfiguration);

      MachineParam.dead_pixel_number_left_edge
      //BaseDataVariableState<float> dead_pixel_number_left_edge = CreateVariable(myFolder, "MachineParam.dead_pixel_number_left_edge", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //dead_pixel_number_left_edge.Description = "Number of dead pixels on the left edge";
      //AMSDataListFloatMelsec.Add(dead_pixel_number_left_edge);

      MachineParam.dead_pixel_number_right_edge
      //BaseDataVariableState<float> dead_pixel_number_right_edge = CreateVariable(myFolder, "MachineParam.dead_pixel_number_right_edge", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //dead_pixel_number_right_edge.Description = "Number of dead pixels on the right edge";
      //AMSDataListFloatMelsec.Add(dead_pixel_number_right_edge);

      MachineParam.dead_pixel_number_middle_edge
      //BaseDataVariableState<float> dead_pixel_number_middle_edge = CreateVariable(myFolder, "MachineParam.dead_pixel_number_middle_edge", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //dead_pixel_number_middle_edge.Description = "Number of dead pixels on the middle edge";
      //AMSDataListFloatMelsec.Add(dead_pixel_number_middle_edge);

      MachineParam.Bvalue
      //BaseDataVariableState<float> Bvalue = CreateVariable(myFolder, "MachineParam.Bvalue", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //Bvalue.Description = "Bvalue parameter";
      //AMSDataListFloatMelsec.Add(Bvalue);

      MachineParam.Kvalue
      //BaseDataVariableState<float> Kvalue = CreateVariable(myFolder, "MachineParam.Kvalue", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //Kvalue.Description = "Kvalue parameter";
      //AMSDataListFloatMelsec.Add(Kvalue);

      MachineParam.ScanningSpeed
      //BaseDataVariableState<float> ScanningSpeed = CreateVariable(myFolder, "MachineParam.ScanningSpeed", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //ScanningSpeed.Description = "Scanning Speed parameter";
      //AMSDataListFloatMelsec.Add(ScanningSpeed);

      MachineParam.SmoothingFactor
      //BaseDataVariableState<float> SmoothingFactor = CreateVariable(myFolder, "MachineParam.SmoothingFactor", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //SmoothingFactor.Description = "Smoothing factor  parameter";
      //AMSDataListFloatMelsec.Add(SmoothingFactor);

      MachineParam.CalibrationTime
      //BaseDataVariableState<float> CalibrationTime = CreateVariable(myFolder, "MachineParam.CalibrationTime", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //CalibrationTime.Description = "Calibration time interval parameter";
      //AMSDataListFloatMelsec.Add(CalibrationTime);

      MachineParam.CloseLoopIntervalMeter
      //BaseDataVariableState<float> CloseLoopIntervalMeter = CreateVariable(myFolder, "MachineParam.CloseLoopIntervalMeter", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //CloseLoopIntervalMeter.Description = "Close loop interval parameter";
      //AMSDataListFloatMelsec.Add(CloseLoopIntervalMeter);

      MachineParam.ActivationSpeed.Threshold
      //BaseDataVariableState<float> ActivationSpeed = CreateVariable(myFolder, "MachineParam.ActivationSpeed.Threshold", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //ActivationSpeed.Description = "The lowest speed of walking";
      //AMSDataListFloatMelsec.Add(ActivationSpeed);

      ProcessVariable.length
      //BaseDataVariableState<float> length = CreateVariable(myFolder, "ProcessVariable.length", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //length.Description = "Coil strip length";
      //AMSDataListFloatMelsec.Add(length);

      ProcessVariable.temperature
      //BaseDataVariableState<float> temperature = CreateVariable(myFolder, "ProcessVariable.temperature", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //temperature.Description = "temperature";
      //AMSDataListFloatMelsec.Add(temperature);

      ProcessVariable.humidity
      //BaseDataVariableState<float> humidity = CreateVariable(myFolder, "ProcessVariable.humidity", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //humidity.Description = "humidity";
      //AMSDataListFloatMelsec.Add(humidity);

      //ProcessVariable.scan times
      BaseDataVariableState<Int16> scantimes = CreateVariable(myFolder, "ProcessVariable.scan times", DataTypeIds.Int16, ValueRanks.Scalar, Int16.MinValue);
      scantimes.Description = "scan counter";
      AMSDataListInt16Melsec.Add(scantimes);

      //ProcessVariable.time
      //BaseDataVariableState<string> time = CreateVariable(myFolder, "ProcessVariable.time", DataTypeIds.String, ValueRanks.Scalar, "2024/10/26");
      //time.Description = "Scan timestamp";
      //AMSDataListStringMelsec.Add(time);


      ProcessVariable.firstStandValue
      //BaseDataVariableState<float> firstStandValue = CreateVariable(myFolder, "ProcessVariable.firstStandValue", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //firstStandValue.Description = "firstStandValue";
      //AMSDataListFloatMelsec.Add(firstStandValue);

      ProcessVariable.firstMeansureValue
      //BaseDataVariableState<float> firstMeansureValue = CreateVariable(myFolder, "ProcessVariable.firstMeansureValue", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //firstMeansureValue.Description = "firstMeansureValue";
      //AMSDataListFloatMelsec.Add(firstMeansureValue);

      ProcessVariable.Correction
      //BaseDataVariableState<float> Correction = CreateVariable(myFolder, "ProcessVariable.Correction", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //Correction.Description = "Correction";
      //AMSDataListFloatMelsec.Add(Correction);

      ProcessVariable.secondStandValue
      //BaseDataVariableState<float> secondStandValue = CreateVariable(myFolder, "ProcessVariable.secondStandValue", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //secondStandValue.Description = "secondStandValue";
      //AMSDataListFloatMelsec.Add(secondStandValue);

      ProcessVariable.secondMeansureValue
      //BaseDataVariableState<float> secondMeansureValue = CreateVariable(myFolder, "ProcessVariable.secondMeansureValue", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //secondMeansureValue.Description = "secondMeansureValue";
      //AMSDataListFloatMelsec.Add(secondMeansureValue);

      ProcessVariable.threeStandValue
      //BaseDataVariableState<float> threeStandValue = CreateVariable(myFolder, "ProcessVariable.threeStandValue", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //threeStandValue.Description = "threeStandValue";
      //AMSDataListFloatMelsec.Add(threeStandValue);

      ProcessVariable.threeMeansureValue
      //BaseDataVariableState<float> threeMeansureValue = CreateVariable(myFolder, "ProcessVariable.threeMeansureValue", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //threeMeansureValue.Description = "threeMeansureValue";
      //AMSDataListFloatMelsec.Add(threeMeansureValue);


       ProcessVariable.ThicknessMeas
      最大支持1500,  
      //for (int iPartition = 1; iPartition <= 1500; iPartition++)
      //{
      //    BaseDataVariableState<float> ThicknessMeasurement = CreateVariable(myFolder, "ProcessVariable.ThicknessMeas[" + iPartition + "]", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //    ThicknessMeasurement.Description = "ThicknessMeasurement " + iPartition;
      //    AMSThicknessMeasDataListFloat.Add(ThicknessMeasurement);
      //}

      最大支持100分区,  第一分区//ProcessVariable.lane
      //for (int iPartition = 1; iPartition <= 100; iPartition++)
      //{
      //    BaseDataVariableState<float> partitionEveryNode = CreateVariable(myFolder, "ProcessVariable.lane[" + iPartition + "]", DataTypeIds.Float, ValueRanks.Scalar, 0f);
      //    partitionEveryNode.Description = "lane" + iPartition;
      //    AMSPartitionDataListFloat.Add(partitionEveryNode);
      //}





  }



  AddPredefinedNode(SystemContext, Melsec);
  #endregion

举例opcua获取三菱数据后,更新至节点的实现:

   #region Modify Melsec value
   public override void ModifyInt16ListMelsec(List<Int16> Int16List)
   {
       if (AMSDataListInt16Melsec != null)
       {
           lock (Lock)
           {
               for (int i = 0; i < Int16List.Count; i++)
               {
                   AMSDataListInt16Melsec[i].Value = Int16List[i];
               }
               // 下面这行代码非常的关键,涉及到更改之后会不会通知到客户端
               for (int i = 0; i < Int16List.Count; i++)
               {
                   AMSDataListInt16Melsec[i].ClearChangeMasks(SystemContext, false);
               }
           }
       }
   }
   public override void ModifyBoolListMelsec(List<bool> BoolList)
   {
       if (AMSDataListBoolMelsec != null)
       {
           lock (Lock)
           {
               for (int i = 0; i < BoolList.Count; i++)
               {
                   AMSDataListBoolMelsec[i].Value = BoolList[i];
               }
               // 下面这行代码非常的关键,涉及到更改之后会不会通知到客户端
               for (int i = 0; i < BoolList.Count; i++)
               {
                   AMSDataListBoolMelsec[i].ClearChangeMasks(SystemContext, false);
               }
           }
       }
   }
   #endregion


http://www.kler.cn/a/389196.html

相关文章:

  • react的创建与书写
  • DApp开发:定制化解决方案与源码部署的一站式指南
  • react 中 FC 模块作用
  • 【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-最大的数
  • 深入剖析【C++继承】:单一继承与多重继承的策略与实践,解锁代码复用和多态的编程精髓,迈向高级C++编程之旅
  • 【贪心算法】No.1---贪心算法(1)
  • PySindy学习
  • 无人机避障——(局部规划方法)DWA(动态窗口法)
  • C#开发流程
  • 1.1 算法基本概念与复杂度分析
  • qt QWebSocketServer详解
  • 第十三届交通运输研究(上海)论坛┆智能网联汽车技术现状与研究实践
  • SpringBoot项目编译报错 类文件具有错误的版本 61.0, 应为 52.0
  • ssh2-sftp-client一键将你的前端项目部署到远程服务器,你只需要专注写bug就好了
  • Rust:GUI 开源框架
  • 数据库范式、MySQL 架构、算法与树的深入解析
  • 实现rtos操作系统 【二】基本任务切换实现
  • 大模型,智能家居的春秋战国之交
  • goframe开发一个企业网站 验证码17
  • DotNet使用CsvHelper快速读取和写入CSV文件的操作方法
  • Apache DolphinScheduler + OceanBase,搭建分布式大数据调度平台的实践
  • 2411d,右值与移动
  • linux入门教程:perl库
  • 【刷题12】ctfshow刷题
  • 面试官:进程与线程的关系和区别到底是什么?
  • 大数据学习10之Hive高级