Bläddra i källkod

Add GalilSocketSimulator.cs, GalilView.xaml and MotorSimulator.cs;

niuyx 1 månad sedan
förälder
incheckning
e9c839326d

+ 2 - 16
CyberX8_RT/Config/Devices/GalilControllerCfg-Simulator.xml

@@ -28,23 +28,9 @@
 			<ForwardSoftwareLimit>28000000</ForwardSoftwareLimit>
 			<NegativeTorqueLimit>130</NegativeTorqueLimit>
 			<PositiveTorqueLimit>20</PositiveTorqueLimit>
-		</GalilAxisConfig>
-		<GalilAxisConfig Name="Vertical" Index="2" Type="Galil">
-			<Speed>100</Speed>
-			<Acceleration>100</Acceleration>
-			<Deceleration>100</Deceleration>
-			<HomingSpeed>100</HomingSpeed>
-			<HomingOffset>0</HomingOffset>
-			<HomingTimeOut>20000</HomingTimeOut>
-			<HomingAcceleration>100</HomingAcceleration>
-			<HomingDeceleration>100</HomingDeceleration>
-			<ReverseSoftwareLimit>-1190000</ReverseSoftwareLimit>
-			<ForwardSoftwareLimit>28000000</ForwardSoftwareLimit>
-			<NegativeTorqueLimit>130</NegativeTorqueLimit>
-			<PositiveTorqueLimit>20</PositiveTorqueLimit>
-		</GalilAxisConfig>
+		</GalilAxisConfig>		
 	</GalilDeviceConfig>
-	<GalilDeviceConfig Module="Loader1" IpAddress="10.0.0.20" Port="58678" SendTimeout="2000" RecvTimeout="2000">
+	<GalilDeviceConfig Module="Loader1" IpAddress="127.0.0.1" Port="58679" SendTimeout="2000" RecvTimeout="2000">
 		<GalilAxisConfig Name="LSA" Index="0" Type="GalilLipsel">
 			<Speed>150000</Speed>
 			<Acceleration>200000</Acceleration>

+ 10 - 3
CyberX8_Simulator/Config/UILayout.xml

@@ -11,6 +11,15 @@
 		<SubView Id="Wago0" Name="Wago0" ViewClass="CyberX8_Simulator.Views.WagoView" Assembly="CyberX8_Simulator" Port ="501"/>
 		<SubView Id="Wago1" Name="Wago1" ViewClass="CyberX8_Simulator.Views.WagoView" Assembly="CyberX8_Simulator" Port ="550"/>
 	</Navigation>
+
+	<Navigation Id="Festo" Name="Festo">
+		<SubView Id="Festo3" Name="Festo3" ViewClass="CyberX8_Simulator.Views.FestoView" Assembly="CyberX8_Simulator" Port="502"/>
+	</Navigation>
+
+	<Navigation Id="Galil" Name="Galil">
+		<SubView Id="Galil" Name="Galil" ViewClass="CyberX8_Simulator.Views.GalilView" Assembly="CyberX8_Simulator" Port="58678"/>
+		<SubView Id="Gali2" Name="Gali2" ViewClass="CyberX8_Simulator.Views.GalilView" Assembly="CyberX8_Simulator" Port="58679"/>
+	</Navigation>
 	
 	<Navigation Id="Power" Name="Power" >		
 		<SubView Id="Power1" Name="Power1" ViewClass="CyberX8_Simulator.Views.PowerSupplierView" Assembly="CyberX8_Simulator" Port="820"/>
@@ -48,7 +57,5 @@
 		<SubView Id="BarcodeReader" Name="BarcodeReader View" ViewClass="CyberX8_Simulator.Views.BarcodeReaderView" Assembly="CyberX8_Simulator"/>
 	</Navigation>
 
-	<Navigation Id="Festo" Name="Festo">
-		<SubView Id="Festo3" Name="Festo3" ViewClass="CyberX8_Simulator.Views.FestoView" Assembly="CyberX8_Simulator" Port="502"/>
-	</Navigation>
+	
 </MECFUI>

+ 8 - 0
CyberX8_Simulator/CyberX8_Simulator.csproj

@@ -80,6 +80,7 @@
       <SubType>Code</SubType>
     </Compile>
     <Compile Include="Devices\FestoSocketSimulator.cs" />
+    <Compile Include="Devices\GalilSocketSimulator.cs" />
     <Compile Include="Devices\SunWayEfemSimulator.cs" />
     <Compile Include="Devices\AIRSYSChillerMock.cs" />
     <Compile Include="Devices\EdwardsPumpMockLL.cs" />
@@ -103,6 +104,9 @@
     <Compile Include="Views\FestoView.xaml.cs">
       <DependentUpon>FestoView.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Views\GalilView.xaml.cs">
+      <DependentUpon>GalilView.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Views\IoViewModelBase.cs" />
     <Compile Include="Views\SimulatorIo1View.xaml.cs">
       <DependentUpon>SimulatorIo1View.xaml</DependentUpon>
@@ -227,6 +231,10 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Views\GalilView.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Views\SimulatorIo1View.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>

+ 28 - 19
CyberX8_Simulator/Devices/FestoSocketSimulator.cs

@@ -42,25 +42,7 @@ namespace CyberX8_Simulator.Devices
         /// <param name="port"></param>
         public FestoSocketSimulator(int port) : base(port)
         {
-            for(int i = 0; i < FESTO_REGISTER_COUNT; i++)
-            {
-                _festoOutputDataDic[i] = 0x00;
-            }
-            string oldXmlPath = PathManager.GetCfgDir();
-            string newXmlPath = oldXmlPath.Replace("CyberX8_Simulator", "CyberX8_RT") + "Devices\\FestoControllerCfg-Simulator.xml";
-            FestoControllerCfg cfg = CustomXmlSerializer.Deserialize<FestoControllerCfg>(new FileInfo(newXmlPath));
-            foreach (FestoDeviceConfig config in cfg.FestoDeviceConfigs)
-            {            
-                if(port == config.Port)
-                {
-                    foreach (FestoDO item in config.FestoDoes)
-                    {
-                        _festoNameIndexDic[item.Name] = item;
-                        string str = $"{item.Address}-{item.Bit}";
-                        _doNameDictionary[str] = item.Name;
-                    }
-                }
-            }
+            InitData(port);            
         }
         /// <summary>
         /// 解析信息
@@ -215,5 +197,32 @@ namespace CyberX8_Simulator.Devices
             string str = $"{address}-{bitNum}";
             return (_doNameDictionary[str], valueBool);
         }
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        private void InitData(int port)
+        {
+            //初始化数据
+            for (int i = 0; i < FESTO_REGISTER_COUNT; i++)
+            {
+                _festoOutputDataDic[i] = 0x00;
+            }
+            //加载对应配置文件 FestoControllerCfg-Simulator.xml
+            string oldXmlPath = PathManager.GetCfgDir();
+            string newXmlPath = oldXmlPath.Replace("CyberX8_Simulator", "CyberX8_RT") + "Devices\\FestoControllerCfg-Simulator.xml";
+            FestoControllerCfg cfg = CustomXmlSerializer.Deserialize<FestoControllerCfg>(new FileInfo(newXmlPath));
+            foreach (FestoDeviceConfig config in cfg.FestoDeviceConfigs)
+            {
+                if (port == config.Port)
+                {
+                    foreach (FestoDO item in config.FestoDoes)
+                    {
+                        _festoNameIndexDic[item.Name] = item;
+                        string str = $"{item.Address}-{item.Bit}";
+                        _doNameDictionary[str] = item.Name;
+                    }
+                }
+            }
+        }
     }
 }

+ 401 - 0
CyberX8_Simulator/Devices/GalilSocketSimulator.cs

@@ -0,0 +1,401 @@
+using Aitex.Common.Util;
+using Aitex.Core.RT.DataCenter;
+using Aitex.Core.RT.Routine;
+using Aitex.Core.Util;
+using CyberX8_Core;
+using MECF.Framework.Common.CommonData.PUF;
+using MECF.Framework.Common.Device.Festo;
+using MECF.Framework.Common.Device.Galil;
+using MECF.Framework.Common.Net;
+using MECF.Framework.Common.Simulator;
+using MECF.Framework.Simulator.Core.Driver;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Xceed.Wpf.Toolkit.Panels;
+
+namespace CyberX8_Simulator.Devices
+{
+    public class GalilSocketSimulator : SocketDeviceSimulator
+    {
+        #region 常量
+        //最大运动轴数量(abcdefg)
+        private const int MAX_AXIS_NUM = 8;
+        //GalilControllerData最大数据长度
+        private const int MAX_CONTROL_DATA_LENGTH = 264;
+        //GalilAxisData最大数据长度
+        private const int MAX_AXIS_DATA_LENGTH = 28;
+        #endregion
+
+        #region 内部变量
+        private IByteTransform byteTransform = new BigEndianByteTransformBase();
+        /// <summary>
+        /// Galil数据
+        /// </summary>
+        private GalilControllerData _galilControlData = new GalilControllerData();
+        /// <summary>
+        /// Axis名称字典(key:axisName, value:index)
+        /// </summary>
+        private Dictionary<string, int> _axisNameIndexDic = new Dictionary<string, int>();
+        /// <summary>
+        /// ModuleName
+        /// </summary>
+        /// <param name="port"></param>
+        private string _moduleName;
+        #endregion
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="port"></param>
+        public GalilSocketSimulator(int port) :base(port)
+        {
+            InitData(port);
+        }
+        /// <summary>
+        /// 解析信息
+        /// </summary>
+        /// <param name="data"></param>
+        protected override void ProcessUnsplitMessage(byte[] data)
+        {
+            string cmdStr = ASCIIEncoding.ASCII.GetString(data);
+            if (CheckCmdValid(cmdStr))
+            {
+                var cmdOperation = AnalyseCommand(cmdStr);
+                byte[] response;
+                if(cmdOperation.Item1 == "QR")
+                {
+                    //Read
+                    response = CreateReadResponse(_galilControlData);
+                }
+                else
+                {
+                    //Write
+                    SetOperation(cmdOperation.Item1, cmdOperation.Item2, cmdOperation.Item3);
+                    response = CreateWriteResponse();
+                }
+                OnWriteMessage(response);
+            }
+            else
+            {
+                OnWriteMessage(CreateError());
+            }                   
+        }
+        /// <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[] CreateReadResponse(GalilControllerData data)
+        {
+            //数据头(4 bytes)
+            int headLength = 4;
+            byte[] result = new byte[headLength + MAX_CONTROL_DATA_LENGTH];
+            result[0] = 0x00;
+            result[1] = 0x00;
+            short dataLength = (short)(headLength + MAX_CONTROL_DATA_LENGTH);
+            Array.Copy(byteTransform.GetBytes(dataLength), 0, result, 2, 2);
+            //数据体(MAX_CONTROL_DATA_LENGTH bytes)
+            Array.Copy(CodeControlData(data), 0, result, 4, MAX_CONTROL_DATA_LENGTH);
+            return result;
+        }
+        /// <summary>
+        /// 写回复
+        /// </summary>
+        /// <param name="flag"></param>
+        /// <param name="channel"></param>
+        /// <param name="command"></param>
+        /// <param name="startAddress"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        private byte[] CreateWriteResponse()
+        {
+            //数据头(1 bytes)
+            int headLength = 1;
+            byte[] result = new byte[headLength];
+            result[0] = 0x3A;
+            return result;
+        }
+        /// <summary>
+        /// 错误回复
+        /// </summary>
+        /// <param name="flag"></param>
+        /// <param name="channel"></param>
+        /// <param name="command"></param>
+        /// <param name="error"></param>
+        /// <returns></returns>
+        private byte[] CreateError()
+        {
+            int headLength = 1;
+            byte[] result = new byte[headLength];
+            //Command not valid in program
+            result[0] = 0x03;
+            return result;
+        }
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        private void InitData(int port)
+        {
+            //初始化AxisData(最大MAX_AXIS_NUM个axis)
+            _galilControlData.GalilAxisDatas = new List<GalilAxisData>();
+            for (int i = 0; i < MAX_AXIS_NUM; i++)
+            {
+                _galilControlData.GalilAxisDatas.Add(new GalilAxisData());
+                _galilControlData.GalilAxisDatas[i].Status = 0x01;
+            }
+            _galilControlData.Inputs  = new byte[10];
+            _galilControlData.Outputs = new byte[10];   
+            _galilControlData.SBlocks = new byte[8];   
+            _galilControlData.TBlocks = new byte[8];
+            //电机数据更新事件
+            MotorSimulator.Instance.OnUpdateVariableValueChanged += UpdataRealTimeMotionData;
+            //加载对应配置文件 GalilControllerCfg-Simulator.xml
+            string oldXmlPath = PathManager.GetCfgDir();
+            string newXmlPath = oldXmlPath.Replace("CyberX8_Simulator", "CyberX8_RT") + "Devices\\GalilControllerCfg-Simulator.xml";
+            GalilControllerCfg cfg = CustomXmlSerializer.Deserialize<GalilControllerCfg>(new FileInfo(newXmlPath));
+            foreach (GalilDeviceConfig config in cfg.GalilDeviceConfigs)
+            {
+                if (port == config.Port)
+                {
+                    _moduleName = config.Module;
+                    foreach (GalilAxisConfig item in config.GalilAxises)
+                    {
+                        _axisNameIndexDic[$"{config.Module}.{item.Name}"] = item.Index;
+                    }
+                }
+            }
+        }
+        /// <summary>
+        /// GalilControllerData编码
+        /// </summary>
+        /// <returns></returns>
+        private byte[] CodeControlData(GalilControllerData ctrlData)
+        {
+            byte[] result = new byte[MAX_CONTROL_DATA_LENGTH];
+            int index = 0;
+            //Sample(2 bytes)
+            Array.Copy(byteTransform.GetBytes(ctrlData.Sample), 0, result, index, 2);
+            index += 2;
+            //Inputs(1*10 bytes)
+            Array.Copy(ctrlData.Inputs, 0, result, index, 10);
+            index += 10;
+            //Outputs(1*10 bytes)
+            Array.Copy(ctrlData.Outputs, 0, result, index, 10);
+            index += 10;
+            //Error Code(1 bytes)         
+            result[13] = ctrlData.ErrorCode;
+            index ++;
+            //General Status(1 bytes)
+            result[14] = ctrlData.Status;
+            index ++;
+            //S Block(2+2+4=8 bytes)
+            Array.Copy(ctrlData.SBlocks, 0, result, 15, 8);
+            index += 8;
+            //T Block(2+2+4=8 bytes)
+            Array.Copy(ctrlData.TBlocks, 0, result, 23, 8);
+            index += 8;
+            //Axis Datas(28 * 8 bytes)
+            for(int i = 0;i < MAX_AXIS_NUM; i++)
+            {
+                Array.Copy(CodeAxisData(ctrlData.GalilAxisDatas[i]), 0, result, index, MAX_AXIS_DATA_LENGTH);
+                index += MAX_AXIS_DATA_LENGTH;
+            }     
+            return result;
+        }
+        /// <summary>
+        /// GalilAxisData编码
+        /// </summary>
+        /// <param name="axisData"></param>
+        /// <returns></returns>
+        private byte[] CodeAxisData(GalilAxisData axisData)
+        {
+            byte[] result = new byte[MAX_AXIS_DATA_LENGTH];
+            int index = 0;
+            //Status(2 bytes)
+            Array.Copy(byteTransform.GetBytes(axisData.Status), 0, result, index, 2);
+            index += 2;
+            //Switches(1 bytes)
+            result[index] = axisData.Switches;
+            index ++;
+            //Stop Code(1 bytes)
+            result[index] = axisData.StopCode;
+            index ++;
+            //Reference Position(4 bytes)
+            Array.Copy(byteTransform.GetBytes(axisData.ReferencePosition), 0, result, index, 4);
+            index += 4;
+            //Motor Position(4 bytes)
+            Array.Copy(byteTransform.GetBytes(axisData.MotorPosition), 0, result, index, 4);
+            index += 4;
+            //Position Error
+            Array.Copy(byteTransform.GetBytes(axisData.PositionError), 0, result, index, 4);
+            index += 4;
+            //Auxiliary Position
+            Array.Copy(byteTransform.GetBytes(axisData.AuxiliaryPosition), 0, result, index, 4);
+            index += 4;
+            //Velocity
+            Array.Copy(byteTransform.GetBytes(axisData.Velocity), 0, result, index, 4);
+            index += 4;
+            //Torque
+            Array.Copy(byteTransform.GetBytes(axisData.Torque), 0, result, index, 2);
+            index += 2;
+            //Analog
+            Array.Copy(byteTransform.GetBytes(axisData.Res), 0, result, index, 2);
+            return result;
+        }
+        /// <summary>
+        /// 解析指令(操作符, 运动轴, 操作数)
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <returns></returns>
+        private (string, char, int) AnalyseCommand(string cmdStr)
+        {
+            var result = ("", ' ', -1);
+            //操作符
+            result.Item1 = cmdStr.Substring(0, 2);
+            //运动轴
+            if (cmdStr.Length >= 4) 
+            {
+                result.Item2 = Convert.ToChar(cmdStr.Substring(2, 1));
+            }
+            //操作数
+            if (cmdStr.Length >= 5 && int.TryParse(cmdStr.Substring(4, cmdStr.Length - 5), out int tmp))
+            {
+               result.Item3 = tmp;      
+            }
+            return result;
+        }
+        /// <summary>
+        /// CMD校验
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckCmdValid(string cmdStr)
+        {
+            //长度
+            if(cmdStr.Length < 3) return false;
+            //;结尾
+            if (!cmdStr.EndsWith(";")) return false;
+            //第1位为A~Z
+            if (cmdStr[0] < 'A' || cmdStr[0] > 'Z') return false;
+            //第2位为A~Z
+            if (cmdStr[1] < 'A' || cmdStr[1] > 'Z') return false;
+            if (cmdStr.Length >= 4)
+            {
+                //axis名称为A~H
+                if (cmdStr[2] < 'A' || cmdStr[2] > 'H') return false;
+                //=
+                if(cmdStr.Length > 4 && !cmdStr.Contains("="))  return false;
+            }
+            
+            return true;
+        }
+        /// <summary>
+        /// 设置操作
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="axis"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        private bool SetOperation(string cmd, char axis, int value)
+        {
+            switch (cmd)
+            {
+                case "SH":
+                    SwitchMotor(axis, true);
+                    break;
+                case "MO":
+                    SwitchMotor(axis, false);
+                    break;
+                case "PR":
+                    SetTargetRelativePosition(axis, value);
+                    break;
+                case "PA":
+                    SetTargetAbsolutePosition(axis, value);
+                    break;
+                case "SP":
+                    break;
+                case "AC":
+                    break;
+                case "DC":
+                    break;
+                case "ST":
+                    break;
+                case "BG":
+                    break;
+                case "HM":
+                    break;
+                case "DP":
+                    break;
+                case "DE":
+                    break;
+                default:
+                    break;
+            }
+            return true;
+        }
+        /// <summary>
+        /// 实时更新电机数据
+        /// </summary>
+        /// <param name="datasDic"></param>
+        private void UpdataRealTimeMotionData(Dictionary<string, CommandMotionData> datasDic)
+        {
+            foreach(var dataItem in datasDic)
+            {
+                if (_axisNameIndexDic.ContainsKey(dataItem.Key))
+                {
+                    UpdateAxisData(_axisNameIndexDic[dataItem.Key], dataItem.Value);
+                }
+            }
+        }
+        /// <summary>
+        /// 更新对应Axis数据
+        /// </summary>
+        /// <param name="index"></param>
+        private void UpdateAxisData(int index, CommandMotionData data)
+        {
+            _galilControlData.GalilAxisDatas[index].Velocity = (int)data.ActualVelocity;
+            _galilControlData.GalilAxisDatas[index].MotorPosition = (int)data.MotorPosition;
+            _galilControlData.GalilAxisDatas[index].ReferencePosition = (int)data.TargetPosition;
+            _galilControlData.GalilAxisDatas[index].Torque = (short)data.ActualTorque;
+        }
+        #region AxisOperation
+        /// <summary>
+        /// 开/关电机
+        /// </summary>
+        /// <param name="axis"></param>
+        /// <param name="flag"></param>
+        private void SwitchMotor(char axis, bool flag)
+        {
+            byte[] data = byteTransform.GetBytes(_galilControlData.GalilAxisDatas[axis - 'A'].Status);
+            data[0] &= 0xFE;
+            data[0] |= (byte)(flag ? 0x00 : 0x01);
+            _galilControlData.GalilAxisDatas[axis - 'A'].Status = byteTransform.TransUInt16(data, 0);
+        }
+        /// <summary>
+        /// 设置目标位置
+        /// </summary>
+        private void SetSpeed(char axis, int value)
+        {
+            _galilControlData.GalilAxisDatas[axis - 'A'].Velocity = value;
+        }
+        /// <summary>
+        /// 设置目标绝对位置
+        /// </summary>
+        private void SetTargetAbsolutePosition(char axis, int value)
+        {
+            _galilControlData.GalilAxisDatas[axis - 'A'].ReferencePosition = value;
+        }
+        /// <summary>
+        /// 设置目标相对位置
+        /// </summary>
+        private void SetTargetRelativePosition(char axis, int value)
+        {
+            _galilControlData.GalilAxisDatas[axis - 'A'].ReferencePosition += value;
+        }
+        #endregion
+    }
+}

+ 56 - 0
CyberX8_Simulator/Views/GalilView.xaml

@@ -0,0 +1,56 @@
+<UserControl x:Class="CyberX8_Simulator.Views.GalilView"
+             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:Height="900" d:Width="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 Orientation="Horizontal" Width="500" Height="50" Margin="100,0,0,0">
+                    <Label Content="DO:" VerticalAlignment="Center"></Label>
+                    <ComboBox  Width="250" Height="30" VerticalContentAlignment="Center" ItemsSource="{Binding DONameItems}" SelectedItem="{Binding DOSelectedItem}" />
+                    <ComboBox  Width="60" Height="30" Margin="5,0,0,0" VerticalContentAlignment="Center" ItemsSource="{Binding DigitalOutputSelected}" SelectedItem="{Binding DOInputValue}" HorizontalContentAlignment="Center"></ComboBox>
+                    <Button Content="DOInput" Height="30" Width="100" Margin="5,10,0,0" Command="{Binding SetDOCommand}"/>
+                </StackPanel>
+            </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>

+ 44 - 0
CyberX8_Simulator/Views/GalilView.xaml.cs

@@ -0,0 +1,44 @@
+using Aitex.Core.UI.MVVM;
+using CyberX8_Simulator.Devices;
+using MECF.Framework.Simulator.Core.Commons;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace CyberX8_Simulator.Views
+{
+    /// <summary>
+    /// GalilView.xaml 的交互逻辑
+    /// </summary>
+    public partial class GalilView : UserControl
+    {
+        public GalilView()
+        {
+            InitializeComponent();
+            this.Loaded += OnViewLoaded;
+        }
+        private void OnViewLoaded(object sender, RoutedEventArgs e)
+        {
+            (DataContext as TimerViewModelBase)?.Start();
+        }
+    }
+    class GalilViewModel : SocketDeviceViewModel
+    {
+        #region 属性
+        public string Title
+        {
+            get { return "Galil Simulator"; }
+        }
+
+        #endregion
+
+        #region 内部变量
+        private GalilSocketSimulator _sim;
+        #endregion
+        public GalilViewModel(string str) : base("GalilViewModel")
+        {
+            int.TryParse(str, out int port);
+            _sim = new GalilSocketSimulator(port);
+            Init(_sim);
+        }
+    }
+}

+ 1 - 0
Framework/Common/Common.csproj

@@ -471,6 +471,7 @@
     <Compile Include="Routine\RoutineRunner.cs" />
     <Compile Include="SCCore\TypedConfigManager.cs" />
     <Compile Include="Beckhoff\Station\BeckhoffStationLocationManager.cs" />
+    <Compile Include="Simulator\MotorSimulator.cs" />
     <Compile Include="Simulator\SimulatorIOMapConfig.cs" />
     <Compile Include="Simulator\SimulatorIOMapItem.cs" />
     <Compile Include="SubstrateTrackings\MaterialInfo.cs" />

+ 89 - 0
Framework/Common/Simulator/MotorSimulator.cs

@@ -0,0 +1,89 @@
+using Aitex.Common.Util;
+using Aitex.Core.Util;
+using MECF.Framework.Common.CommonData.PUF;
+using MECF.Framework.Common.Device.Galil;
+using System.Collections.Generic;
+using System.IO;
+
+namespace MECF.Framework.Common.Simulator
+{
+    /// <summary>
+    /// 电机运动模拟器
+    /// </summary>
+    public class MotorSimulator : Singleton<MotorSimulator>
+    {
+        #region 内部变量
+        /// <summary>
+        /// 定时器
+        /// </summary>
+        private PeriodicJob _periodicJob;
+        /// <summary>
+        /// 电机数据字典(key:Name(module.name),value:Datas)
+        /// </summary>
+        private Dictionary<string, CommandMotionData> _motorNameDataDic = new Dictionary<string, CommandMotionData>();
+        #endregion
+
+        #region 属性
+
+        #endregion
+
+        //delegate
+        #region Delegate
+        public delegate void UpdateVariableValueChanged(Dictionary<string, CommandMotionData> datasDic);
+        #endregion
+
+        #region 事件
+        /// <summary>
+        /// 变量变更事件
+        /// </summary>
+        public event UpdateVariableValueChanged OnUpdateVariableValueChanged;
+        #endregion
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        public void Initialize()
+        {
+
+            _periodicJob = new PeriodicJob(100, OnTimer, "Motor Simulator Timer", false);//***打开
+            Init();
+        }
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        private void Init()
+        {
+            ////加载对应配置文件 GalilControllerCfg-Simulator.xml,初始化数据字典
+            string oldXmlPath = PathManager.GetCfgDir();
+            string newXmlPath = oldXmlPath.Replace("CyberX8_Simulator", "CyberX8_RT") + "Devices\\GalilControllerCfg-Simulator.xml";
+            GalilControllerCfg cfg = CustomXmlSerializer.Deserialize<GalilControllerCfg>(new FileInfo(newXmlPath));
+            foreach (GalilDeviceConfig config in cfg.GalilDeviceConfigs)
+            {               
+                foreach (GalilAxisConfig item in config.GalilAxises)
+                {
+                    _motorNameDataDic[$"{config.Module}.{item.Name}"] = new CommandMotionData();
+                }
+            }
+        }
+        /// <summary>
+        /// 定时器执行
+        /// </summary>
+        /// <returns></returns>
+        private bool OnTimer()
+        {
+            //电机运动物理模型
+
+            return true;
+        }
+        /// <summary>
+        /// 通知Galil模块数据变化
+        /// </summary>
+        /// <param name="data"></param>
+        private void UpdateVariableValue(Dictionary<string, CommandMotionData> datasDic)
+        {
+            if (OnUpdateVariableValueChanged != null)
+            {
+                OnUpdateVariableValueChanged(datasDic);
+            }
+        }
+    }
+}

+ 2 - 0
Framework/UICore/E95Template/CenterView.xaml.cs

@@ -16,6 +16,7 @@ using Aitex.Core.RT.Log;
 using MECF.Framework.UI.Core.Applications;
 using Autofac;
 using Aitex.Core.RT.Device;
+using MECF.Framework.Common.Simulator;
 
 namespace Aitex.Core.UI.View.Frame
 {
@@ -34,6 +35,7 @@ namespace Aitex.Core.UI.View.Frame
         public void CreateView(List<ViewItem> views )
         {
             SimulatorCommManager.Instance.Initialize();
+            MotorSimulator.Instance.Initialize();
             foreach (ViewItem item in views)
             {
                 if (item.SubView == null || item.SubView.Count == 0)