Browse Source

add wago simulator

chenzk 2 months ago
parent
commit
d27f0c35ea

+ 5 - 5
CyberX8_RT/Config/Devices/WagoControllerCfg-Simulator.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <WagoControllerConfig>
-	<WagoDeviceConfig Module="Wago0" IpAddress="10.0.0.12" Port="502" SendTimeout="2000" RecvTimeout="2000" Channel="1">
+	<WagoDeviceConfig Module="Wago0" IpAddress="127.0.0.1" Port="501" SendTimeout="2000" RecvTimeout="2000" Channel="1">
 		<Dig_In>
 			<DIGroup Name="N5">
 				<DI Name="DI0"  Address="0" Invert="false"/>
@@ -32,9 +32,9 @@
 			<DOGroup Name="N7">
 				<DO Name="DO0"  Address="512" Invert="false"/>
 				<DO Name="c_LoaderA_LS_Vacuum"  Address="513" Invert="false"/>
-				<DO Name="DO3"  Address="514" Invert="false"/>
+				<DO Name="c_LoaderB_LS_Vacuum"  Address="514" Invert="false"/>
 				<DO Name="DO4"  Address="515" Invert="false"/>
-				<DO Name="DO5"  Address="516" Invert="false"/>
+				<DO Name="c_DPUF_A_CHUCK_A_RELEASE"  Address="516" Invert="false"/>
 				<DO Name="DO6"  Address="517" Invert="false"/>
 				<DO Name="DO7"  Address="518" Invert="false"/>
 				<DO Name="DO8"  Address="519" Invert="false"/>
@@ -62,9 +62,9 @@
 		<Ano_In>
 			<AIGroup Name="N1">
 				<AI Name="r_LoaderA_LS_Vacuum_anlg"  Address="0" Scaling="0=3276.7,-757=16383.5" DataType="short"/>
-				<AI Name="AI2"  Address="1" DataType="short"/>
+				<AI Name="r_LoaderB_LS_Vacuum_anlg"  Address="1" Scaling="0=3276.7,-757=16383.5" DataType="short"/>
 				<AI Name="AI3"  Address="2" DataType="short"/>
-				<AI Name="AI4"  Address="3" DataType="short"/>
+				<AI Name="r_DPUF_A_CHUCK_A_VAC"  Address="3" Scaling="0=3276.7,-757=16383.5" DataType="short"/>
 				<AI Name="AI5"  Address="4" DataType="short"/>
 				<AI Name="AI6"  Address="5" DataType="short"/>
 				<AI Name="AI7"  Address="6" DataType="short"/>

+ 5 - 0
CyberX8_Simulator/Config/UILayout.xml

@@ -6,6 +6,11 @@
 
 	  <SubView Id="Efem" Name="EFEM" ViewClass="CyberX8_Simulator.Views.Simu_SunWayEfemView" Assembly="CyberX8_Simulator" />
   </Navigation>
+
+	<Navigation Id="Wago" Name="Wago" >
+		<SubView Id="Wago0" Name="Wago0" ViewClass="CyberX8_Simulator.Views.WagoView" Assembly="CyberX8_Simulator" Port ="501"/>
+	</Navigation>
+	
 	<Navigation Id="Power" Name="Power" >		
 		<SubView Id="Power1" Name="Power1" ViewClass="CyberX8_Simulator.Views.PowerSupplierView" Assembly="CyberX8_Simulator" Port="820"/>
 		<SubView Id="Power2" Name="Power2" ViewClass="CyberX8_Simulator.Views.PowerSupplierView" Assembly="CyberX8_Simulator" Port="821"/>

+ 8 - 0
CyberX8_Simulator/CyberX8_Simulator.csproj

@@ -91,6 +91,7 @@
     <Compile Include="Devices\TMSimulatorServer.cs" />
     <Compile Include="Devices\VceSimulator.cs" />
     <Compile Include="Devices\VPASimulator.cs" />
+	<Compile Include="Devices\WagoSocketSimulator.cs" />
     <Compile Include="Instances\SystemConfig.cs" />
     <Compile Include="JetChamber.cs" />
     <Compile Include="JetsiuTM.cs" />
@@ -148,6 +149,9 @@
     <Compile Include="Views\Simu_TMView.xaml.cs">
       <DependentUpon>Simu_TMView.xaml</DependentUpon>
     </Compile>
+	  <Compile Include="Views\WagoView.xaml.cs">
+      <DependentUpon>WagoView.xaml</DependentUpon>
+    </Compile>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Devices\AdTecGeneratorMock.cs" />
@@ -271,6 +275,10 @@
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
+	   <Page Include="Views\WagoView.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Views\Simu_EfemView.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>

+ 232 - 0
CyberX8_Simulator/Devices/WagoSocketSimulator.cs

@@ -0,0 +1,232 @@
+using MECF.Framework.Common.Net;
+using MECF.Framework.Simulator.Core.Driver;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace CyberX8_Simulator.Devices
+{
+    public class WagoSocketSimulator : SocketDeviceSimulator
+    {
+        private const short WRITE_DO_STARTADDRESS = 0x0200;
+        private const short WRITE_AO_STARTADDRESS = 0x0200;
+       
+        
+        private IByteTransform byteTransform = new BigEndianByteTransformBase();
+
+        private byte[] DOBytes = new byte[100];
+
+        private short[] AOShorts = new short[50];
+
+        private byte[] DIBytes = new byte[100];
+
+        private short[] AIShorts = new short[50];
+        /// <summary>
+        /// 写DO锁
+        /// </summary>
+        private object _writeDOLocker = new object();
+        /// <summary>
+        /// 写AO锁
+        /// </summary>
+        private object _writeAOLocker = new object();
+
+        public WagoSocketSimulator(int port):base(port) 
+        {
+            InitializeData();
+        }
+        
+        /// <summary>
+        /// 初始化数组数据
+        /// </summary>
+        private void InitializeData()
+        {
+            AIShorts[3] = 0x1388;
+        }
+
+        #region 功能方法
+        /// <summary>
+        /// 将长度为8的二进制byte数组转成对应十六进制byte值(大端模式)
+        /// </summary>
+        /// <param name="byteArray"></param>
+        /// <returns></returns>
+        public byte ConvertByteArrayToHex(byte[] byteArray)
+        {
+            byte result = 0;
+            // 先将 byte 数组转换为二进制数
+            int binaryValue = 0;
+            for (int i = 0; i < 8; i++)
+            {  
+                binaryValue |= (byteArray[i] << (7 - i));
+            }
+
+            // 逆转二进制数
+            int reversedValue = 0;
+            for (int i = 0; i < 8; i++)
+            {
+                reversedValue |= ((binaryValue >> i) & 1) << (7 - i);
+            }
+            // 转换为十六进制byte
+            if (byte.TryParse(reversedValue.ToString("X2"), System.Globalization.NumberStyles.HexNumber, null, out result))
+            {
+               return result;
+            }
+            return 0;
+        }
+        /// <summary>
+        /// 将short数组转成长度两倍的byte数组
+        /// </summary>
+        /// <param name="shortArray"></param>
+        /// <returns></returns>
+        private byte[] ConvertShortArrayToByteArray(short[] shortArray)
+        {
+            byte[] byteArray = new byte[shortArray.Length * 2];
+            for (int i = 0; i < shortArray.Length; i++)
+            {
+                byte[] tempBytes = BitConverter.GetBytes(shortArray[i]);
+                Array.Copy(tempBytes, 0, byteArray, i * 2, 2);
+            }
+            return byteArray;
+        }
+        #endregion
+        
+        protected override void ProcessUnsplitMessage(byte[] data)
+        {
+            byte command = data[7];
+            if (command == 0x01) //读DO
+            {
+                short flag = byteTransform.TransInt16(data, 0);
+                byte channel = data[6];
+                short startAddress = byteTransform.TransInt16(data, 8);
+                short bitCount = byteTransform.TransInt16(data, 10);
+                byte byteCount = (byte)(bitCount / 8 + 1);
+                byte[] bytes = new byte[byteCount];
+                for(int i = 0; i < byteCount;i++)
+                {
+                    byte[] tempbytes = new byte[8];
+                    Array.Copy(DOBytes,8 * i, tempbytes, 0, 8);
+                    bytes[i] = ConvertByteArrayToHex(tempbytes);
+                }
+                OnWriteMessage(CreateReadDigitalResponse(flag, channel, command, byteCount, bytes));
+                return;
+            }
+            else if(command == 0x03)//读AO
+            {
+                short flag = byteTransform.TransInt16(data, 0);
+                byte channel = data[6];
+                short startAddress = byteTransform.TransInt16(data, 8);
+                short registerCount = byteTransform.TransInt16(data, 10);
+                short[] shorts = new short[registerCount];//获取指定寄存器里的内容
+                Array.Copy(AOShorts, 0, shorts, 0, registerCount);
+                byte[] bytes = new byte[registerCount * 2];
+                bytes = ConvertShortArrayToByteArray(shorts); //转入长度为shorts数组长度两倍的bytes数组中
+                OnWriteMessage(CreateReadAnalogyResponse(flag, channel, command, (byte)registerCount, bytes));
+                return;
+            }
+            else if (command == 0x02)//读DI
+            {
+                short flag = byteTransform.TransInt16(data, 0);
+                byte channel = data[6];
+                short startAddress = byteTransform.TransInt16(data, 8);
+                short bitCount = byteTransform.TransInt16(data, 10);
+                byte byteCount = (byte)(bitCount / 8 + 1);
+                byte[] bytes = new byte[byteCount];
+                for (int i = 0; i < byteCount; i++)
+                {
+                    byte[] tempbytes = new byte[8];
+                    Array.Copy(DIBytes, 8 * i, tempbytes, 0, 8);
+                    bytes[i] = ConvertByteArrayToHex(tempbytes);
+                }
+                OnWriteMessage(CreateReadDigitalResponse(flag, channel, command, byteCount, bytes));
+                return;
+            }
+            else if (command == 0x04)//读AI
+            {
+                short flag = byteTransform.TransInt16(data, 0);
+                byte channel = data[6];
+                short startAddress = byteTransform.TransInt16(data, 8);
+                short registerCount = byteTransform.TransInt16(data, 10);
+                short[] shorts = new short[registerCount];//获取指定寄存器里的内容
+                Array.Copy(AIShorts, 0, shorts, 0, registerCount);
+                byte[] bytes = new byte[registerCount * 2];
+                bytes = ConvertShortArrayToByteArray(shorts); //转入长度为shorts数组两倍的bytes数组中
+                OnWriteMessage(CreateReadAnalogyResponse(flag, channel, command, (byte)registerCount, bytes));
+                return;
+            }
+            else if (command == 0x05)//写DO
+            {
+                short startAddress = byteTransform.TransInt16(data, 8);
+                int position = startAddress - WRITE_DO_STARTADDRESS;
+                bool status = data[10] == 0xFF ? true : false;
+                lock (_writeDOLocker)
+                {
+                    DOBytes[position] = status ? (byte)1 : (byte)0;
+                }
+                OnWriteMessage(data); //原消息返回
+                return;
+            }
+            else if (command == 0x06)//写AO
+            {
+                short startAddress = byteTransform.TransInt16(data, 8);
+                int position = startAddress - WRITE_AO_STARTADDRESS;
+                short value = byteTransform.TransInt16(data, 10);
+                lock (_writeAOLocker)
+                {
+                    AOShorts[position] = value;
+                }
+                OnWriteMessage(data); //原消息返回
+                return;
+            }
+        }
+        /// <summary>
+        /// 回复读数字量
+        /// </summary>
+        /// <param name="flag"></param>
+        /// <param name="channel"></param>
+        /// <param name="command"></param>
+        /// <param name="byteCount"></param>
+        /// <param name="values"></param>
+        /// <returns></returns>
+        private byte[] CreateReadDigitalResponse(short flag, byte channel, byte command, byte byteCount, byte[] values)
+        {
+            byte[] bytes = new byte[7 + 2 + values.Length]; //回复字节长度,前面7个字节固定长度 + functionCode一个字节 + byteCount一个字节+values.length个字节
+            Array.Copy(byteTransform.GetBytes(flag), 0, bytes, 0, 2);
+            bytes[2] = 0x00;
+            bytes[3] = 0x00;
+            short dataLength = (short)(3 + values.Length);
+            Array.Copy(byteTransform.GetBytes(dataLength), 0, bytes, 4, 2);
+            bytes[6] = channel;
+            bytes[7] = command;
+            bytes[8] = byteCount;
+            Array.Copy(values, 0, bytes, 9, values.Length);
+            return bytes;
+        }
+        /// <summary>
+        /// 回复读模拟量
+        /// </summary>
+        /// <param name="flag"></param>
+        /// <param name="channel"></param>
+        /// <param name="command"></param>
+        /// <param name="registerCount"></param>
+        /// <param name="values"></param>
+        /// <returns></returns>
+        private byte[] CreateReadAnalogyResponse(short flag, byte channel, byte command, byte registerCount, byte[] values)
+        {
+            byte[] bytes = new byte[7 + 2 + 2 * registerCount]; //回复字节长度,前面7个字节固定长度 + functionCode一个字节 + byteCount一个字节+registerCount*2个字节(一个寄存器占两个字节)
+            Array.Copy(byteTransform.GetBytes(flag), 0, bytes, 0, 2);
+            bytes[2] = 0x00;
+            bytes[3] = 0x00;
+            short dataLength = (short)(3 + 2 * registerCount);
+            Array.Copy(byteTransform.GetBytes(dataLength), 0, bytes, 4, 2);
+            bytes[6] = channel;
+            bytes[7] = command;
+            bytes[8] = (byte)(2 * registerCount);
+            Array.Copy(values, 0, bytes, 9, values.Length);
+            return bytes;
+        }
+
+       
+    }
+}

+ 50 - 0
CyberX8_Simulator/Views/WagoView.xaml

@@ -0,0 +1,50 @@
+<UserControl x:Class="CyberX8_Simulator.Views.WagoView"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:CyberX8_Simulator.Views"
+             xmlns:commons="clr-namespace:MECF.Framework.Simulator.Core.Commons;assembly=MECF.Framework.Simulator.Core"
+             mc:Ignorable="d" 
+             d:DesignHeight="900"  d:DesignWidth="1200">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="100"></RowDefinition>
+            <RowDefinition Height="50"></RowDefinition>
+            <RowDefinition />
+            <RowDefinition Height="10"></RowDefinition>
+        </Grid.RowDefinitions>
+
+        <commons:SocketTitleView Grid.Row="0"></commons:SocketTitleView>
+
+        <Grid  Grid.Row="1">
+            <StackPanel Orientation="Horizontal" Width="1200">
+                <Button Content="Clear Log" Width="100" Height="35"   Command="{Binding ClearLogCommand}"></Button>
+            </StackPanel>
+        </Grid>
+
+        <DataGrid Grid.Row="2" FontSize="16" AutoGenerateColumns="False" CanUserAddRows="False" CanUserResizeRows="False" CanUserSortColumns="False"
+              ItemsSource="{Binding TransactionLogItems}"
+                  Width="1200" VerticalAlignment="Top" MaxHeight="780">
+            <DataGrid.Columns>
+                <DataGridTextColumn Header="Time" Width="200" IsReadOnly="True" Binding="{Binding OccurTime, UpdateSourceTrigger=PropertyChanged}" />
+                <DataGridTextColumn Header="Incoming" Width="500" IsReadOnly="True"  Binding="{Binding Incoming, UpdateSourceTrigger=PropertyChanged}">
+                    <DataGridTextColumn.ElementStyle>
+                        <Style TargetType="TextBlock">
+                            <Setter Property="TextWrapping" Value="Wrap" />
+                            <Setter Property="Height" Value="auto" />
+                        </Style>
+                    </DataGridTextColumn.ElementStyle>
+                </DataGridTextColumn>
+                <DataGridTextColumn Header="Outgoing" Width="500" IsReadOnly="True"  Binding="{Binding Outgoing, UpdateSourceTrigger=PropertyChanged}">
+                    <DataGridTextColumn.ElementStyle>
+                        <Style TargetType="TextBlock">
+                            <Setter Property="TextWrapping" Value="Wrap" />
+                            <Setter Property="Height" Value="auto" />
+                        </Style>
+                    </DataGridTextColumn.ElementStyle>
+                </DataGridTextColumn>
+            </DataGrid.Columns>
+        </DataGrid>
+    </Grid>
+</UserControl>

+ 55 - 0
CyberX8_Simulator/Views/WagoView.xaml.cs

@@ -0,0 +1,55 @@
+using Aitex.Core.UI.MVVM;
+using CyberX8_Simulator.Devices;
+using MECF.Framework.Simulator.Core.Commons;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace CyberX8_Simulator.Views
+{
+    /// <summary>
+    /// WagoView.xaml 的交互逻辑
+    /// </summary>
+    public partial class WagoView : UserControl
+    {
+        public WagoView()
+        {
+            InitializeComponent();
+            this.Loaded += OnViewLoaded;
+        }
+
+        private void OnViewLoaded(object sender, RoutedEventArgs e)
+        {
+            (DataContext as TimerViewModelBase)?.Start();
+        }
+    }
+
+    class WagoViewModel : SocketDeviceViewModel
+    {
+        public string Title
+        {
+            get { return "Wago Simulator"; }
+        }
+
+
+        private WagoSocketSimulator _sim;
+        public WagoViewModel(string str) : base("WagoViewModel")
+        {
+            int.TryParse(str, out int port);
+            _sim = new WagoSocketSimulator(port);
+            Init(_sim);
+        }
+    }
+
+}

+ 1 - 1
Framework/Common/Device/Wago/WagoControllerCfgManager.cs

@@ -493,7 +493,7 @@ namespace MECF.Framework.Common.Device.Wago
         }
 
         /// <summary>
-        /// 设置DO数值
+        /// 设置AO数值
         /// </summary>
         /// <param name="aoName"></param>
         /// <param name="value"></param>

+ 1 - 1
Framework/Common/Device/Wago/WagoModbusDevice.cs

@@ -193,7 +193,7 @@ namespace MECF.Framework.Common.Device.Wago
             }
         }
         /// <summary>
-        /// 写入DO数值
+        /// 写入AO数值
         /// </summary>
         /// <param name="address"></param>
         /// <param name="value"></param>