Skip to content

Commit

Permalink
增加日志显式功能(并暴露给脚本引擎)
Browse files Browse the repository at this point in the history
  • Loading branch information
newbienewbie committed Mar 28, 2023
1 parent bad7ec9 commit 3cb19cb
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 124 deletions.
10 changes: 10 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ S7.WriteReal(6, 3040, 4.8)

### API

#### `S7`

Python可以使用的`S7`其实是一个`IS7ServerService`接口对象:

```C#
Expand Down Expand Up @@ -141,6 +143,14 @@ public interface IS7ServerService
}
```

#### `Logger`

还暴露一个`Logger`对象,用于追加日志:
```
void LogInfo(string content);
void LogError(string content);
```



## 已知问题
Expand Down
4 changes: 1 addition & 3 deletions src/S7SvrSim/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ namespace S7Svr.Simulator
/// </summary>
public partial class App : Application
{



public IHost _host { get; private set; }
public IServiceProvider ServiceProvider { get; internal set; }

Expand Down Expand Up @@ -64,6 +61,7 @@ private void ConfigureServices(HostBuilderContext ctx, IServiceCollection servic
services.AddSingleton<ConfigSnap7ServerVM>();
services.AddSingleton<PyScriptRunner>();
services.AddSingleton<ConfigPyEngineVM>();
services.AddSingleton<MsgLoggerVM>();
services.AddSingleton<MainWindow>();
services.AddSingleton<IS7ServerService, S7ServerService>();
services.AddSingleton<MainVM>();
Expand Down
245 changes: 128 additions & 117 deletions src/S7SvrSim/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,138 +6,149 @@
xmlns:local="clr-namespace:S7Svr.Simulator"
xmlns:uc="clr-namespace:S7Svr.Simulator.UserControls"
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:viewmodels="clr-namespace:S7Svr.Simulator.ViewModels"
xmlns:viewmodels="clr-namespace:S7Svr.Simulator.ViewModels"
xmlns:usercontrols="clr-namespace:S7SvrSim.UserControls"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=viewmodels:MainVM}"
Title="Siemens PLC 通讯模拟器"
Height="450" Width="800">

<TabControl TabStripPlacement="Left">
<TabControl.Resources>
<Style TargetType="TabItem" BasedOn="{StaticResource MaterialDesignNavigationRailTabItem}">
<Style.Triggers>
</Style.Triggers>
</Style>
<Style TargetType="Label" BasedOn="{StaticResource MaterialDesignLabel}"></Style>
<Style TargetType="TextBox" BasedOn="{StaticResource MaterialDesignTextBox}"></Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource MaterialDesignTextBlock}">
</Style>
</TabControl.Resources>
<TabItem Header="DB配置">
<TabItem.Resources>
<Style TargetType="Grid" >
<Setter Property="Margin" Value="4,8,4,4"></Setter>
Height="850" Width="800">


<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="200"></RowDefinition>
</Grid.RowDefinitions>
<TabControl TabStripPlacement="Left">
<TabControl.Resources>
<Style TargetType="TabItem" BasedOn="{StaticResource MaterialDesignNavigationRailTabItem}">
<Style.Triggers>
</Style.Triggers>
</Style>
</TabItem.Resources>

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition ></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>


<Label Content="IP Address" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Column="0" ></Label>
<TextBox Text="{Binding ConfigVM.IpAddress.Value}"
<Style TargetType="Label" BasedOn="{StaticResource MaterialDesignLabel}"></Style>
<Style TargetType="TextBox" BasedOn="{StaticResource MaterialDesignTextBox}"></Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource MaterialDesignTextBlock}">
</Style>
</TabControl.Resources>
<TabItem Header="DB配置">
<TabItem.Resources>
<Style TargetType="Grid" >
<Setter Property="Margin" Value="4,8,4,4"></Setter>
</Style>
</TabItem.Resources>

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition ></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>


<Label Content="IP Address" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Column="0" ></Label>
<TextBox Text="{Binding ConfigVM.IpAddress.Value}"
Width="100" VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" />
<Button Content="启动" Command="{Binding CmdStartServer}"
<Button Content="启动" Command="{Binding CmdStartServer}"
Width="100" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="0" Grid.Column="2"/>
<Button Content="停止" Command="{Binding CmdStopServer}"
<Button Content="停止" Command="{Binding CmdStopServer}"
Width="100" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="3"/>


<DataGrid ItemsSource="{Binding ConfigVM.DBConfigs}"
<DataGrid ItemsSource="{Binding ConfigVM.DBConfigs}"
CanUserDeleteRows="True"
CanUserAddRows="True" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="7" Margin="0,25,0,0" Grid.RowSpan="2" >
<DataGrid.Style>
<Style TargetType="DataGrid" BasedOn="{StaticResource MaterialDesignDataGrid}">
<Style.Triggers>
<DataTrigger Binding="{Binding RunningVM.RunningStatus.Value}" Value="True">
<Setter Property="IsEnabled" Value="False"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RunningVM.RunningStatus.Value}" Value="False">
<Setter Property="IsEnabled" Value="True"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
<DataGrid.Columns>
</DataGrid.Columns>
</DataGrid>

</Grid>
</TabItem>
<TabItem Header="DB命令">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>

<Label Content="IpAddress" Grid.Row="0" Grid.Column="0" ></Label>
<TextBlock Text="{Binding ConfigVM.IpAddress.Value}" Grid.Row="0" Grid.Column="1"></TextBlock>

<uc:OperationsCtrl DataContext="{Binding OperationVM}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" ></uc:OperationsCtrl>

<ScrollViewer VerticalScrollBarVisibility="Auto" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<ItemsControl ItemsSource="{Binding RunningVM.RunningsItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander >
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DBNumber,StringFormat={}DBNumber\={0}}"></TextBlock>
<TextBlock Text="{Binding DBSize,StringFormat={}\, DBSize\={0}}"></TextBlock>
</StackPanel>
</Expander.Header>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>

</Grid>
</TabItem>

<TabItem Header="PyEngine" DataContext="{Binding ConfigPyEngineVM}">
<GroupBox>
<GroupBox.Header>Search Paths</GroupBox.Header>
<DataGrid.Style>
<Style TargetType="DataGrid" BasedOn="{StaticResource MaterialDesignDataGrid}">
<Style.Triggers>
<DataTrigger Binding="{Binding RunningVM.RunningStatus.Value}" Value="True">
<Setter Property="IsEnabled" Value="False"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RunningVM.RunningStatus.Value}" Value="False">
<Setter Property="IsEnabled" Value="True"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
<DataGrid.Columns>
</DataGrid.Columns>
</DataGrid>

</Grid>
</TabItem>
<TabItem Header="DB命令">

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="32"></RowDefinition>
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding PyEngineSearchPaths}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding .}" IsReadOnly="True" ></TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Command="{Binding CmdSelectModulePath}" Content="选择路径" Grid.Row="1" Grid.Column="0" ></Button>
<TextBox Text="{Binding SelectedModulePath.Value}" VerticalAlignment="Stretch" TextAlignment="Justify" Grid.Row="1" Grid.Column="1"></TextBox>
<Button Content="提交" Command="{Binding CmdSubmitSelectPath}" Grid.Row="1" Grid.Column="2" ></Button>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>

<Label Content="IpAddress" Grid.Row="0" Grid.Column="0" ></Label>
<TextBlock Text="{Binding ConfigVM.IpAddress.Value}" Grid.Row="0" Grid.Column="1"></TextBlock>

<uc:OperationsCtrl DataContext="{Binding OperationVM}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" ></uc:OperationsCtrl>

<ScrollViewer VerticalScrollBarVisibility="Auto" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<ItemsControl ItemsSource="{Binding RunningVM.RunningsItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander >
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DBNumber,StringFormat={}DBNumber\={0}}"></TextBlock>
<TextBlock Text="{Binding DBSize,StringFormat={}\, DBSize\={0}}"></TextBlock>
</StackPanel>
</Expander.Header>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>

</Grid>
</GroupBox>
</TabItem>
</TabControl>
</TabItem>

<TabItem Header="PyEngine" DataContext="{Binding ConfigPyEngineVM}">
<GroupBox>
<GroupBox.Header>Search Paths</GroupBox.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="32"></RowDefinition>
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding PyEngineSearchPaths}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding .}" IsReadOnly="True" ></TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Command="{Binding CmdSelectModulePath}" Content="选择路径" Grid.Row="1" Grid.Column="0" ></Button>
<TextBox Text="{Binding SelectedModulePath.Value}" VerticalAlignment="Stretch" TextAlignment="Justify" Grid.Row="1" Grid.Column="1"></TextBox>
<Button Content="提交" Command="{Binding CmdSubmitSelectPath}" Grid.Row="1" Grid.Column="2" ></Button>
</Grid>
</GroupBox>
</TabItem>
</TabControl>

<usercontrols:MsgLoggerCtrl DataContext="{Binding LoggerVM }" Grid.Row="3" Grid.Column="1" >
</usercontrols:MsgLoggerCtrl>
</Grid>

</Window>
7 changes: 6 additions & 1 deletion src/S7SvrSim/Services/PyScriptRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.Scripting.Hosting;
using S7Svr.Simulator.ViewModels;
using S7SvrSim.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -13,13 +14,15 @@ namespace S7SvrSim.Services
public class PyScriptRunner
{
private readonly IS7ServerService _s7ServerSvc;
private readonly MsgLoggerVM _loggerVM;
private readonly ILogger<PyScriptRunner> _logger;
public ScriptEngine PyEngine { get; }
private ScriptScope pyScope = null;

public PyScriptRunner(IS7ServerService s7ServerSvc, ILogger<PyScriptRunner> logger)
public PyScriptRunner(IS7ServerService s7ServerSvc, MsgLoggerVM loggerVM ,ILogger<PyScriptRunner> logger)
{
this._s7ServerSvc = s7ServerSvc;
this._loggerVM = loggerVM;
this._logger = logger;
this.PyEngine = Python.CreateEngine();
}
Expand All @@ -33,13 +36,15 @@ public void RunFile(string scriptpath)
pyScope = PyEngine.CreateScope();
pyScope.SetVariable("s7_server_svc", this._s7ServerSvc);
pyScope.SetVariable("S7", this._s7ServerSvc);
pyScope.SetVariable("Logger", this._loggerVM);
pyScope.SetVariable("__PY_ENGINE__", this.PyEngine);
}

var source = PyEngine.CreateScriptSourceFromFile(scriptpath);
var code = source.Compile();

code.Execute(pyScope);

}
}
}
8 changes: 7 additions & 1 deletion src/S7SvrSim/Services/S7ServerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using MediatR;
using Microsoft.Extensions.Logging;
using S7Svr.Simulator.Messages;
using S7SvrSim.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -14,14 +15,16 @@ namespace S7Svr.Simulator.ViewModels
public class S7ServerService : IDisposable, IS7ServerService
{
private readonly RunningSnap7ServerVM _runningVM;
private readonly MsgLoggerVM _loggerVM;
private readonly ILogger<S7ServerService> _logger;
protected virtual IMediator _mediator { get; set; }
protected virtual FutureTech.Snap7.S7Server S7Server { get; set; }

public S7ServerService(IMediator mediator, RunningSnap7ServerVM runningVM, ILogger<S7ServerService> logger)
public S7ServerService(IMediator mediator, RunningSnap7ServerVM runningVM, MsgLoggerVM loggerVM, ILogger<S7ServerService> logger)
{
this._mediator = mediator;
this._runningVM = runningVM;
this._loggerVM = loggerVM;
this._logger = logger;
}

Expand Down Expand Up @@ -53,6 +56,8 @@ public async Task StartServerAsync()
this.S7Server.RegisterArea(FutureTech.Snap7.S7Server.srvAreaDB, db.DBNumber, ref buffer, db.DBSize);
}
this.S7Server.StartTo(_runningVM.IpAddress.Value);

this._loggerVM.AddLogMsg(new LogMessage(DateTime.Now, LogLevel.Information, "[+]服务启动..."));
}
catch (Exception ex)
{
Expand All @@ -71,6 +76,7 @@ public async Task StopServerAsync()
{
this.S7Server?.Stop();
this.S7Server = null;
this._loggerVM.AddLogMsg(new LogMessage(DateTime.Now, LogLevel.Information, "[!]服务停止..."));
}
catch (Exception ex)
{
Expand Down
Loading

0 comments on commit 3cb19cb

Please sign in to comment.