├── .github
├── FUNDING.yml
└── workflows
│ └── dotnet-core.yml
├── .gitignore
├── .template.config
└── template.json
├── CreateNugetPackage.bat
├── LICENSE
├── README.md
├── TwincatWpfHMI.template.nuspec
├── WpfApp.Gui
├── App.xaml
├── App.xaml.cs
├── Contents.Designer.cs
├── Contents.de.Designer.cs
├── Contents.de.resx
├── Contents.resx
├── Converters
│ ├── BoolToBrushConverter.cs
│ ├── BoolToStyleConverter.cs
│ ├── BoolToVisibilityConverter.cs
│ ├── ConnectionStateToVisibilityConverter.cs
│ ├── MinimalRoleToVisibilityConverter.cs
│ └── RoleToVisibilityConverter.cs
├── Design
│ ├── DesignPlcEventService.cs
│ ├── DesignPlcProvider.cs
│ └── DesignSettingsProvider.cs
├── GuiModuleCatalog.cs
├── Resources
│ └── icon.png
├── Services
│ └── PresentationService.cs
├── ViewModelLocator.cs
├── ViewModels
│ ├── Basics
│ │ ├── PlcErrorBarViewModel.cs
│ │ ├── PlcErrorDetailsViewModel.cs
│ │ └── PlcVariableViewModel.cs
│ ├── GraphViewModel.cs
│ ├── MainViewModel.cs
│ ├── MainWindowViewModel.cs
│ ├── SettingsViewModel.cs
│ └── ViewModelBase.cs
├── Views
│ ├── Basics
│ │ ├── PlcButton.xaml
│ │ ├── PlcButton.xaml.cs
│ │ ├── PlcErrorBar.xaml
│ │ ├── PlcErrorBar.xaml.cs
│ │ ├── PlcErrorDetails.xaml
│ │ ├── PlcErrorDetails.xaml.cs
│ │ ├── PlcSignalOkNok.xaml
│ │ ├── PlcSignalOkNok.xaml.cs
│ │ ├── PlcSignalOnOff.xaml
│ │ ├── PlcSignalOnOff.xaml.cs
│ │ ├── PlcUserControl.cs
│ │ ├── PlcVariable.xaml
│ │ └── PlcVariable.xaml.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ └── Pages
│ │ ├── GraphView.xaml
│ │ ├── GraphView.xaml.cs
│ │ ├── MainView.xaml
│ │ ├── MainView.xaml.cs
│ │ ├── SettingsView.xaml
│ │ └── SettingsView.xaml.cs
└── WpfApp.Gui.csproj
├── WpfApp.Interfaces
├── Commons
│ ├── IInitializable.cs
│ ├── IInstanceCreator.cs
│ └── IViewModelFactory.cs
├── Constants.cs
├── Enums
│ ├── Role.cs
│ └── Severity.cs
├── Exceptions
│ └── LoginFailedException.cs
├── Extensions
│ ├── DisposableExtensions.cs
│ └── RandomExtensions.cs
├── Hardware
│ ├── IPlc.cs
│ └── MockPlc.cs
├── Models
│ ├── DatabaseObject.cs
│ ├── PlcEvent.cs
│ └── User.cs
├── Services
│ ├── IDatabaseService.cs
│ ├── IDirectoryService.cs
│ ├── IPlcEventLogService.cs
│ ├── IPlcEventService.cs
│ ├── IPlcProvider.cs
│ ├── ISelectionService.cs
│ ├── ISettingsProvider.cs
│ └── IUserService.cs
├── Settings
│ ├── ApplicationSetting.cs
│ ├── CultureSetting.cs
│ ├── ErrorCodeDescription.cs
│ ├── ErrorCodeSetting.cs
│ ├── ErrorSetting.cs
│ ├── HardwareSetting.cs
│ ├── PlcSetting.cs
│ └── SettingRoot.cs
├── Ui
│ ├── IPresentationService.cs
│ └── IViewModel.cs
└── WpfApp.Interfaces.csproj
├── WpfApp.Logic
├── Hardware
│ ├── BeckhoffConversions.cs
│ └── BeckhoffPlc.cs
├── LogicModuleCatalog.cs
├── Services
│ ├── DatabaseService.cs
│ ├── DirectoryService.cs
│ ├── PlcEventLogService.cs
│ ├── PlcEventService.cs
│ ├── PlcProvider.cs
│ ├── SelectionService.cs
│ ├── SettingsService.cs
│ └── UserService.cs
└── WpfApp.Logic.csproj
├── WpfApp.sln
├── WpfApp
├── Program.cs
├── WpfApp.csproj
└── icon.ico
└── docs
└── example1.gif
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: fbarresi
4 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet-core.yml:
--------------------------------------------------------------------------------
1 | name: .NET Core Build
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: windows-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 3.1.101
20 | - name: Install dependencies
21 | run: dotnet restore
22 | - name: Build
23 | run: dotnet build --configuration Release --no-restore
24 | - name: Test
25 | run: dotnet test --no-restore --verbosity normal
26 |
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
--------------------------------------------------------------------------------
/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "Federico Barresi",
4 | "classifications": [
5 | "Common", "WPF"
6 | ],
7 | "preferNameDirectory": "true",
8 | "sourceName": "WpfApp",
9 | "identity": "TwincatWpfHMI.template",
10 | "name": "Twincat WPF HMI",
11 | "shortName": "tchmi",
12 | "tags": {
13 | "language": "C#",
14 | "type": "project"
15 | }
16 | }
--------------------------------------------------------------------------------
/CreateNugetPackage.bat:
--------------------------------------------------------------------------------
1 | nuget pack TwincatWpfHMI.template.nuspec -NoDefaultExcludes
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Federico Barresi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # twincat-wpf-boilerplate
2 | an hackable template for building fabulous wpf hmi with twincat
3 |
4 | 
5 |
6 | ## Main features
7 |
8 | - :heavy_check_mark: An easy-to-use alternative to other Beckhoff HMI engines (web or plc based)
9 | - :heavy_check_mark: runs on every network connected device
10 | - :heavy_check_mark: Professional application (Embedded Database, cleaned architecture, open source)
11 | - :heavy_check_mark: Low code and XAML based: get nice usable dashboards in seconds only with XAML design
12 | - :heavy_check_mark: Fully hackable, customizable and extendable with c#
13 |
14 |
15 | ## Example
16 |
17 | 
18 |
19 |
20 | ## Quickstart
21 |
22 | ### Create your application
23 |
24 | 1. Install the template via console or powershell
25 | ```
26 | dotnet new --install TwincatWpfHMI.template
27 | ```
28 |
29 | 2. Create your application with name `MyFancyHMI`
30 |
31 | ```
32 | dotnet new tchmi -n MyFancyHMI -o MyFancyWorkingDirectory
33 | ```
34 |
35 | ### Design your HMI in XAML
36 |
37 | #### With included controls
38 |
39 | `todo`
40 |
41 | #### With your custom controls
42 |
43 | Create new user controls by:
44 |
45 | - creating your custom controls and view models in `WpfApp.Gui\View` and `WpfApp.Gui\ViewModels`
46 | - implementing `PlcUserControl` in XAML and in code-behind
47 | - adding one of the included DataContext (`DataContext="{Binding PlcVariableViewModel, Source={StaticResource Locator}}"`)
48 |
49 | XAML (.xaml)
50 | ```xml
51 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | ```
69 |
70 | Code-behind (xaml.cs)
71 | ```csharp
72 | public partial class PlcVariableView : PlcUserControl
73 | {
74 | public PlcVariableView()
75 | {
76 | InitializeComponent();
77 | }
78 | }
79 | ```
80 |
81 | ### Run the application
82 |
83 | `todo`
84 |
85 |
--------------------------------------------------------------------------------
/TwincatWpfHMI.template.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TwincatWpfHMI.template
5 | 1.0.10
6 |
7 | A TwinCAT HMI as a WPF application.
8 |
9 | https://github.com/fbarresi/twincat-wpf-boilerplate
10 | Federico Barresi
11 |
12 |
13 |
14 | MIT
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/WpfApp.Gui/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | True
11 | False
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/WpfApp.Gui/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace WpfApp.Gui
4 | {
5 | ///
6 | /// Interaction logic for App.xaml
7 | ///
8 | public partial class App : Application
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Contents.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace WpfApp.Gui.Resources {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Contents {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Contents() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfApp.Gui.Contents", typeof(Contents).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to Save.
65 | ///
66 | internal static string Save {
67 | get {
68 | return ResourceManager.GetString("Save", resourceCulture);
69 | }
70 | }
71 |
72 | ///
73 | /// Looks up a localized string similar to Settings.
74 | ///
75 | internal static string Settings {
76 | get {
77 | return ResourceManager.GetString("Settings", resourceCulture);
78 | }
79 | }
80 |
81 | ///
82 | /// Looks up a localized string similar to Button.
83 | ///
84 | internal static string TestButton {
85 | get {
86 | return ResourceManager.GetString("TestButton", resourceCulture);
87 | }
88 | }
89 |
90 | ///
91 | /// Looks up a localized string similar to Description.
92 | ///
93 | internal static string TestSignalDescription {
94 | get {
95 | return ResourceManager.GetString("TestSignalDescription", resourceCulture);
96 | }
97 | }
98 |
99 | ///
100 | /// Looks up a localized string similar to Lamp.
101 | ///
102 | internal static string TestSignalLabel {
103 | get {
104 | return ResourceManager.GetString("TestSignalLabel", resourceCulture);
105 | }
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Contents.de.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace WpfApp.Gui {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Contents_de {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Contents_de() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfApp.Gui.Contents.de", typeof(Contents_de).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to Speichern.
65 | ///
66 | internal static string Save {
67 | get {
68 | return ResourceManager.GetString("Save", resourceCulture);
69 | }
70 | }
71 |
72 | ///
73 | /// Looks up a localized string similar to Einstellungen.
74 | ///
75 | internal static string Settings {
76 | get {
77 | return ResourceManager.GetString("Settings", resourceCulture);
78 | }
79 | }
80 |
81 | ///
82 | /// Looks up a localized string similar to Knopf.
83 | ///
84 | internal static string TestButton {
85 | get {
86 | return ResourceManager.GetString("TestButton", resourceCulture);
87 | }
88 | }
89 |
90 | ///
91 | /// Looks up a localized string similar to Beschreibung.
92 | ///
93 | internal static string TestSignalDescription {
94 | get {
95 | return ResourceManager.GetString("TestSignalDescription", resourceCulture);
96 | }
97 | }
98 |
99 | ///
100 | /// Looks up a localized string similar to Lampe.
101 | ///
102 | internal static string TestSignalLabel {
103 | get {
104 | return ResourceManager.GetString("TestSignalLabel", resourceCulture);
105 | }
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Contents.de.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Beschreibung
23 |
24 |
25 | Lampe
26 |
27 |
28 | Speichern
29 |
30 |
31 | Einstellungen
32 |
33 |
34 | Knopf
35 |
36 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Contents.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | text/microsoft-resx
11 |
12 |
13 | 1.3
14 |
15 |
16 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
17 |
18 |
19 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
20 |
21 |
22 | Lamp
23 |
24 |
25 | Description
26 |
27 |
28 | Save
29 |
30 |
31 | Settings
32 |
33 |
34 | Button
35 |
36 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Converters/BoolToBrushConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows;
4 | using System.Windows.Data;
5 | using System.Windows.Media;
6 |
7 | namespace WpfApp.Gui.Converters
8 | {
9 | public class BoolToBrushConverter : IValueConverter
10 | {
11 | public Brush IfTrue { get; set; } = Brushes.Green;
12 | public Brush IfFalse { get; set; } = Brushes.Red;
13 |
14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
15 | {
16 | if (value is bool)
17 | {
18 | return (bool) value ? IfTrue : IfFalse;
19 | }
20 |
21 | return DependencyProperty.UnsetValue;
22 |
23 | }
24 |
25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
26 | {
27 | return DependencyProperty.UnsetValue;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Converters/BoolToStyleConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows;
4 | using System.Windows.Data;
5 | using System.Windows.Media;
6 |
7 | namespace WpfApp.Gui.Converters
8 | {
9 | public class BoolToStyleConverter : IValueConverter
10 | {
11 | public Style IfTrue { get; set; }
12 | public Style IfFalse { get; set; }
13 |
14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
15 | {
16 | if (value is bool)
17 | {
18 | return (bool) value ? IfTrue : IfFalse;
19 | }
20 |
21 | return DependencyProperty.UnsetValue;
22 |
23 | }
24 |
25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
26 | {
27 | return DependencyProperty.UnsetValue;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Converters/BoolToVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows;
4 | using System.Windows.Data;
5 |
6 | namespace WpfApp.Gui.Converters
7 | {
8 | public class BoolToVisibilityConverter : IValueConverter
9 | {
10 | public Visibility IfTrue { get; set; } = Visibility.Visible;
11 | public Visibility IfFalse { get; set; } = Visibility.Collapsed;
12 |
13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
14 | {
15 | if (value is bool)
16 | {
17 | return (bool) value ? IfTrue : IfFalse;
18 | }
19 |
20 | return DependencyProperty.UnsetValue;
21 |
22 | }
23 |
24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
25 | {
26 | return DependencyProperty.UnsetValue;
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Converters/ConnectionStateToVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows;
4 | using System.Windows.Data;
5 | using TwinCAT;
6 |
7 | namespace WpfApp.Gui.Converters
8 | {
9 | public class ConnectionStateToVisibilityConverter : IValueConverter
10 | {
11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
12 | {
13 | if (value is ConnectionState)
14 | {
15 | return (ConnectionState) value == ConnectionState.Connected ? Visibility.Collapsed : Visibility.Visible;
16 | }
17 |
18 | return DependencyProperty.UnsetValue;
19 |
20 | }
21 |
22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
23 | {
24 | return DependencyProperty.UnsetValue;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Converters/MinimalRoleToVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Linq;
5 | using System.Windows;
6 | using System.Windows.Data;
7 | using WpfApp.Interfaces.Enums;
8 |
9 | namespace WpfApp.Gui.Converters
10 | {
11 | public class MinimalRoleToVisibilityConverter : IValueConverter
12 | {
13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
14 | {
15 | var parsed = Enum.TryParse((string)parameter, out Role parsedRole);
16 |
17 | if (value is List && parsed)
18 | {
19 | return ((List) value).Any(r => r <= parsedRole) ? Visibility.Visible : Visibility.Collapsed;
20 | }
21 |
22 | return Visibility.Collapsed;
23 |
24 | }
25 |
26 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
27 | {
28 | return DependencyProperty.UnsetValue;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Converters/RoleToVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Windows;
5 | using System.Windows.Data;
6 | using WpfApp.Interfaces.Enums;
7 |
8 | namespace WpfApp.Gui.Converters
9 | {
10 | public class RoleToVisibilityConverter : IValueConverter
11 | {
12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
13 | {
14 | var parsed = Enum.TryParse((string)parameter, out Role parsedRole);
15 |
16 | if (value is List && parsed)
17 | {
18 | return ((List) value).Contains(parsedRole) ? Visibility.Visible : Visibility.Collapsed;
19 | }
20 |
21 | return Visibility.Collapsed;
22 |
23 | }
24 |
25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
26 | {
27 | return DependencyProperty.UnsetValue;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Design/DesignPlcEventService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Linq;
3 | using WpfApp.Interfaces.Models;
4 | using WpfApp.Interfaces.Services;
5 |
6 | namespace WpfApp.Gui.Design
7 | {
8 | internal class DesignPlcEventService : IPlcEventService
9 | {
10 | public IObservable ActiveEvents => Observable.Never();
11 | public IObservable LatestEvent => Observable.Never();
12 | }
13 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Design/DesignPlcProvider.cs:
--------------------------------------------------------------------------------
1 | using WpfApp.Interfaces.Hardware;
2 | using WpfApp.Interfaces.Services;
3 |
4 | namespace WpfApp.Gui.Design
5 | {
6 | internal class DesignPlcProvider : IPlcProvider
7 | {
8 | public IPlc GetHardware(string name)
9 | {
10 | return new MockPlc();
11 | }
12 |
13 | public IPlc GetHardware()
14 | {
15 | return new MockPlc();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Design/DesignSettingsProvider.cs:
--------------------------------------------------------------------------------
1 | using WpfApp.Interfaces.Services;
2 | using WpfApp.Interfaces.Settings;
3 |
4 | namespace WpfApp.Gui.Design
5 | {
6 | internal class DesignSettingsProvider : ISettingsProvider
7 | {
8 | public SettingRoot SettingRoot => new SettingRoot();
9 | public void SaveSettings()
10 | {
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/GuiModuleCatalog.cs:
--------------------------------------------------------------------------------
1 | using MahApps.Metro.Controls.Dialogs;
2 | using Ninject.Modules;
3 | using WpfApp.Gui.Services;
4 | using WpfApp.Interfaces.Ui;
5 |
6 | namespace WpfApp.Gui
7 | {
8 | public class GuiModuleCatalog : NinjectModule
9 | {
10 | public override void Load()
11 | {
12 | Bind().To().InSingletonScope();
13 | Bind().To().InSingletonScope();
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fbarresi/twincat-wpf-boilerplate/8f013ee26513634c483efe762ca8fcd3670c87c5/WpfApp.Gui/Resources/icon.png
--------------------------------------------------------------------------------
/WpfApp.Gui/Services/PresentationService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Linq;
3 | using System.Reactive.Subjects;
4 | using WpfApp.Interfaces.Commons;
5 | using WpfApp.Interfaces.Ui;
6 |
7 | namespace WpfApp.Gui.Services
8 | {
9 | public class PresentationService : IPresentationService, IDisposable
10 | {
11 | private readonly IViewModelFactory viewModelFactory;
12 |
13 | private readonly BehaviorSubject activeViewModelSubject = new BehaviorSubject(null);
14 |
15 | public PresentationService(IViewModelFactory viewModelFactory)
16 | {
17 | this.viewModelFactory = viewModelFactory;
18 | }
19 |
20 | public void SwitchActiveViewModel(IViewModel viewModel)
21 | {
22 | activeViewModelSubject.Value?.Dispose();
23 | activeViewModelSubject.OnNext(viewModel);
24 | }
25 |
26 | public void SwitchActiveViewModel(Type viewModelType)
27 | {
28 | var viewModel = viewModelFactory.CreateViewModel(viewModelType);
29 | SwitchActiveViewModel(viewModel);
30 | }
31 |
32 | public void SwitchActiveViewModel() where T : IViewModel
33 | {
34 | var viewModel = viewModelFactory.CreateViewModel();
35 | SwitchActiveViewModel(viewModel);
36 | }
37 |
38 | public IObservable ActiveViewModel => activeViewModelSubject.AsObservable();
39 |
40 | public void Dispose()
41 | {
42 | activeViewModelSubject.Value?.Dispose();
43 | activeViewModelSubject.OnCompleted();
44 | activeViewModelSubject.Dispose();
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/ViewModelLocator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Ninject;
3 | using Ninject.Parameters;
4 | using WpfApp.Gui.ViewModels;
5 | using WpfApp.Gui.ViewModels.Basics;
6 | using WpfApp.Interfaces;
7 | using WpfApp.Interfaces.Commons;
8 | using WpfApp.Interfaces.Ui;
9 | using IInitializable = WpfApp.Interfaces.Commons.IInitializable;
10 |
11 | namespace WpfApp.Gui
12 | {
13 | public class ViewModelLocator : IViewModelFactory, IInstanceCreator
14 | {
15 | protected readonly IKernel Kernel;
16 |
17 | private static ViewModelLocator s_Instance;
18 |
19 | public ViewModelLocator()
20 | {
21 | BindServices();
22 | }
23 |
24 | private void BindServices()
25 | {
26 | Kernel.Bind().To();
27 | }
28 |
29 | public ViewModelLocator(IKernel kernel)
30 | {
31 | Kernel = kernel;
32 | kernel.Bind().ToConstant(this);
33 | BindServices();
34 | }
35 |
36 | public static IInstanceCreator DesignInstanceCreator => s_Instance ?? (s_Instance = new ViewModelLocator());
37 |
38 | public static IViewModelFactory DesignViewModelFactory => s_Instance ?? (s_Instance = new ViewModelLocator());
39 |
40 | public MainWindowViewModel MainWindowViewModel => Kernel.Get();
41 |
42 | public PlcVariableViewModel PlcVariableViewModel => CreateViewModel();
43 |
44 | public T Create()
45 | {
46 | var newObject = Kernel.Get();
47 | return newObject;
48 | }
49 |
50 | public T CreateInstance(ConstructorArgument[] arguments)
51 | {
52 | var vm = Kernel.Get(arguments);
53 | InitializeInitialziable(vm as IInitializable);
54 | return vm;
55 | }
56 |
57 | public TVm CreateViewModel(T model)
58 | {
59 | var vm = Kernel.Get(new ConstructorArgument(@"model", model));
60 | InitializeInitialziable(vm as IInitializable);
61 | return vm;
62 | }
63 |
64 | public TVm CreateViewModel()
65 | {
66 | var vm = Kernel.Get();
67 | InitializeInitialziable(vm as IInitializable);
68 | return vm;
69 | }
70 |
71 | public IViewModel CreateViewModel(Type viewModelType)
72 | {
73 | var vm = Kernel.Get(viewModelType) as IViewModel;
74 | InitializeInitialziable(vm);
75 | return vm;
76 | }
77 |
78 | private static void InitializeInitialziable(IInitializable initializable)
79 | {
80 | initializable?.Init();
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/ViewModels/Basics/PlcErrorBarViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Linq;
3 | using WpfApp.Gui.Design;
4 | using WpfApp.Interfaces.Extensions;
5 | using WpfApp.Interfaces.Models;
6 | using WpfApp.Interfaces.Services;
7 |
8 | namespace WpfApp.Gui.ViewModels.Basics
9 | {
10 | public class PlcErrorBarViewModel : ViewModelBase
11 | {
12 | private readonly IPlcEventService eventService;
13 | private bool hasErrors;
14 | private PlcEvent error;
15 |
16 | public PlcErrorBarViewModel(IPlcEventService eventService)
17 | {
18 | this.eventService = eventService;
19 | }
20 | protected override void Initialize()
21 | {
22 | eventService.LatestEvent
23 | .ObserveOnDispatcher()
24 | .Do(b => HasErrors = (b == null))
25 | .Where(e => e != null)
26 | .Do(e => Error = e)
27 | .Subscribe()
28 | .AddDisposableTo(Disposables);
29 |
30 | }
31 |
32 | public PlcEvent Error
33 | {
34 | get => error;
35 | set
36 | {
37 | if (Equals(value, error)) return;
38 | error = value;
39 | raisePropertyChanged();
40 | }
41 | }
42 |
43 | public bool HasErrors
44 | {
45 | get => hasErrors;
46 | set
47 | {
48 | if (value == hasErrors) return;
49 | hasErrors = value;
50 | raisePropertyChanged();
51 | }
52 | }
53 | }
54 |
55 | internal class DesignPlcErrorBarViewModel : PlcErrorBarViewModel
56 | {
57 | public DesignPlcErrorBarViewModel() : base(new DesignPlcEventService())
58 | {
59 | Init();
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/ViewModels/Basics/PlcErrorDetailsViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.ObjectModel;
3 | using System.Reactive.Linq;
4 | using DynamicData;
5 | using WpfApp.Gui.Design;
6 | using WpfApp.Interfaces.Extensions;
7 | using WpfApp.Interfaces.Models;
8 | using WpfApp.Interfaces.Services;
9 |
10 | namespace WpfApp.Gui.ViewModels.Basics
11 | {
12 | public class PlcErrorDetailsViewModel : ViewModelBase
13 | {
14 | private readonly IPlcEventService plcEventService;
15 | private ObservableCollection activeErrors = new ObservableCollection();
16 |
17 | public PlcErrorDetailsViewModel(IPlcEventService plcEventService)
18 | {
19 | this.plcEventService = plcEventService;
20 | }
21 | protected override void Initialize()
22 | {
23 | plcEventService.ActiveEvents
24 | .Do(_ => ActiveErrors.Clear())
25 | .Do(events => ActiveErrors.AddRange(events))
26 | .Subscribe()
27 | .AddDisposableTo(Disposables)
28 | ;
29 | }
30 |
31 | public ObservableCollection ActiveErrors
32 | {
33 | get => activeErrors;
34 | set
35 | {
36 | if (Equals(value, activeErrors)) return;
37 | activeErrors = value;
38 | raisePropertyChanged();
39 | }
40 | }
41 | }
42 |
43 | internal class DesignPlcErrorDetailsViewModel : PlcErrorDetailsViewModel
44 | {
45 | public DesignPlcErrorDetailsViewModel() : base(new DesignPlcEventService())
46 | {
47 | Init();
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/ViewModels/Basics/PlcVariableViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using System.Reactive.Subjects;
6 | using System.Threading.Tasks;
7 | using ReactiveUI;
8 | using TwinCAT;
9 | using WpfApp.Interfaces.Extensions;
10 | using WpfApp.Interfaces.Hardware;
11 | using WpfApp.Interfaces.Services;
12 |
13 | namespace WpfApp.Gui.ViewModels.Basics
14 | {
15 | public class PlcVariableViewModel : ViewModelBase
16 | {
17 | private readonly IPlcProvider provider;
18 | private string variablePath = string.Empty;
19 | private string plcName;
20 | private string label;
21 | private string description;
22 | private readonly SerialDisposable variableSubscriptions = new SerialDisposable();
23 | private readonly BehaviorSubject canSetVariable = new BehaviorSubject(false);
24 | private ConnectionState connectionState;
25 | private object rawValue;
26 | private IPlc plc;
27 | private object setParameter;
28 | private string valueFormat;
29 |
30 | public ReactiveCommand SetVariable { get; set; }
31 | public PlcVariableViewModel(IPlcProvider provider)
32 | {
33 | this.provider = provider;
34 | }
35 | protected override void Initialize()
36 | {
37 | variableSubscriptions.AddDisposableTo(Disposables);
38 |
39 | SetVariable = ReactiveCommand
40 | .CreateFromTask(WriteVariable)
41 | .AddDisposableTo(Disposables)
42 | ;
43 |
44 | Logger.Debug("PlcVariableViewModel view model initialized!");
45 | }
46 |
47 | private async Task WriteVariable(Unit arg)
48 | {
49 | if (plc == null) return Unit.Default;
50 | await plc.Write(VariablePath, SetParameter);
51 | return Unit.Default;
52 | }
53 |
54 | public string VariablePath
55 | {
56 | get => variablePath;
57 | set
58 | {
59 | if (value == variablePath) return;
60 | variablePath = value;
61 | raisePropertyChanged();
62 | }
63 | }
64 |
65 | public string PlcName
66 | {
67 | get => plcName;
68 | set
69 | {
70 | if (value == plcName) return;
71 | plcName = value;
72 | raisePropertyChanged();
73 | }
74 | }
75 |
76 | public string Label
77 | {
78 | get => label;
79 | set
80 | {
81 | if (value == label) return;
82 | label = value;
83 | raisePropertyChanged();
84 | }
85 | }
86 |
87 | public string Description
88 | {
89 | get => description;
90 | set
91 | {
92 | if (value == description) return;
93 | description = value;
94 | raisePropertyChanged();
95 | }
96 | }
97 |
98 | public object RawValue
99 | {
100 | get => rawValue;
101 | set
102 | {
103 | if (value == rawValue) return;
104 | rawValue = value;
105 | raisePropertyChanged();
106 | }
107 | }
108 |
109 | public ConnectionState ConnectionState
110 | {
111 | get => connectionState;
112 | set
113 | {
114 | if (value == connectionState) return;
115 | connectionState = value;
116 | raisePropertyChanged();
117 | }
118 | }
119 |
120 | public object SetParameter
121 | {
122 | get => setParameter;
123 | set
124 | {
125 | if (Equals(value, setParameter)) return;
126 | setParameter = value;
127 | raisePropertyChanged();
128 | }
129 | }
130 |
131 | public string ValueFormat
132 | {
133 | get => valueFormat;
134 | set
135 | {
136 | if (value == valueFormat) return;
137 | valueFormat = value;
138 | raisePropertyChanged();
139 | }
140 | }
141 |
142 | public void SetupVariableViewModel(string plcName, string variablePath, string label, string description,
143 | object setParameter, string valueFormat)
144 | {
145 | var subscriptions = new CompositeDisposable();
146 |
147 | Logger.Debug($"Setting up variable Plc Variable for {variablePath}");
148 |
149 | plc = plcName == null ? provider.GetHardware() : provider.GetHardware(plcName);
150 | PlcName = plcName;
151 | VariablePath = variablePath;
152 | Label = label;
153 | Description = description;
154 | SetParameter = setParameter;
155 | ValueFormat = valueFormat;
156 |
157 | plc.ConnectionState
158 | .DistinctUntilChanged()
159 | .ObserveOnDispatcher()
160 | .Do(cs => ConnectionState = cs)
161 | .Subscribe()
162 | .AddDisposableTo(subscriptions)
163 | ;
164 |
165 | plc.CreateNotification(variablePath)
166 | .ObserveOnDispatcher()
167 | .Do(r => RawValue = r)
168 | .Subscribe()
169 | .AddDisposableTo(subscriptions)
170 | ;
171 |
172 | plc.ConnectionState
173 | .DistinctUntilChanged()
174 | .ObserveOnDispatcher()
175 | .Select(state => state == ConnectionState.Connected)
176 | .Subscribe(canSetVariable.OnNext)
177 | .AddDisposableTo(Disposables)
178 | ;
179 |
180 | variableSubscriptions.Disposable = subscriptions;
181 | }
182 | }
183 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/ViewModels/GraphViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.ObjectModel;
3 | using System.Reactive.Linq;
4 | using System.Windows;
5 | using OxyPlot;
6 | using OxyPlot.Series;
7 | using WpfApp.Gui.Design;
8 | using WpfApp.Interfaces.Extensions;
9 | using WpfApp.Interfaces.Hardware;
10 | using WpfApp.Interfaces.Services;
11 | using WpfApp.Interfaces.Settings;
12 |
13 | namespace WpfApp.Gui.ViewModels
14 | {
15 | public class GraphViewModel: ViewModelBase
16 | {
17 | private readonly IPlcProvider plcProvider;
18 | private readonly ApplicationSetting setting;
19 | private PlotModel plotModel;
20 |
21 | public GraphViewModel(IPlcProvider plcProvider, ApplicationSetting setting)
22 | {
23 | this.plcProvider = plcProvider;
24 | this.setting = setting;
25 | Title = "Graph View";
26 | }
27 |
28 | protected override void Initialize()
29 | {
30 | PlotModel = new PlotModel();
31 | PlotModel.Series.Add(new LineSeries());
32 |
33 | var plc = plcProvider.GetHardware();
34 |
35 | plc.CreateNotification(setting.DoubleSignalName)
36 | .ObserveOnDispatcher()
37 | .Do(d =>
38 | {
39 | var plotModelSeries = PlotModel.Series[0] as LineSeries;
40 | plotModelSeries?.Points.Add(new DataPoint(plotModelSeries.Points.Count, d));
41 | if(plotModelSeries?.Points.Count > 100) plotModelSeries?.Points.RemoveAt(0);
42 | PlotModel.InvalidatePlot(true);
43 | })
44 | .Subscribe()
45 | .AddDisposableTo(Disposables)
46 | ;
47 |
48 | Logger?.Debug("Initialized Graph view");
49 | }
50 |
51 | public PlotModel PlotModel
52 | {
53 | get => plotModel;
54 | set
55 | {
56 | if(plotModel == value) return;
57 | raisePropertyChanged();
58 | plotModel = value;
59 | }
60 | }
61 | }
62 |
63 | internal class DesignGraphViewModel : GraphViewModel
64 | {
65 | public DesignGraphViewModel() : base(new DesignPlcProvider(), new ApplicationSetting())
66 | {
67 | Init();
68 | }
69 |
70 |
71 | }
72 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/ViewModels/MainViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Reactive.Linq;
2 | using ReactiveUI;
3 | using TwinCAT;
4 | using WpfApp.Gui.Design;
5 | using WpfApp.Interfaces.Services;
6 | using WpfApp.Interfaces.Settings;
7 |
8 | namespace WpfApp.Gui.ViewModels
9 | {
10 | public class MainViewModel : ViewModelBase
11 | {
12 |
13 | public MainViewModel()
14 | {
15 | Title = "Main View";
16 | }
17 | protected override void Initialize()
18 | {
19 | Logger.Debug("Main view initialized!");
20 | }
21 |
22 | }
23 |
24 | internal class DesignMainViewModel : MainViewModel
25 | {
26 | public DesignMainViewModel() : base()
27 | {
28 | Init();
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/ViewModels/MainWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Reactive;
4 | using System.Reactive.Linq;
5 | using System.Threading.Tasks;
6 | using MahApps.Metro.Controls.Dialogs;
7 | using ReactiveUI;
8 | using TwinCAT;
9 | using WpfApp.Gui.ViewModels.Basics;
10 | using WpfApp.Interfaces.Commons;
11 | using WpfApp.Interfaces.Exceptions;
12 | using WpfApp.Interfaces.Extensions;
13 | using WpfApp.Interfaces.Services;
14 | using WpfApp.Interfaces.Settings;
15 | using WpfApp.Interfaces.Ui;
16 | using WPFLocalizeExtension.Engine;
17 |
18 | namespace WpfApp.Gui.ViewModels
19 | {
20 | public class MainWindowViewModel : ViewModelBase
21 | {
22 | private readonly IDialogCoordinator dialogCoordinator;
23 | private readonly IPlcProvider provider;
24 | private readonly IPresentationService presentationService;
25 | private readonly SettingRoot settingRoot;
26 | private readonly IViewModelFactory viewModelFactory;
27 | private bool sideMenuOpen;
28 | private ObservableAsPropertyHelper helper;
29 | private ViewModelBase activeViewModel;
30 | private CultureInfo selectedLanguage;
31 |
32 | public ReactiveCommand ToggleMenu { get; set; }
33 | public ReactiveCommand Login { get; set; }
34 | public ReactiveCommand Logout { get; set; }
35 | public ReactiveCommand SwitchToViewModel { get; set; }
36 | public ReactiveCommand ChangeLanguageCmd { get; set; }
37 |
38 | public CultureInfo SelectedLanguage
39 | {
40 | get => selectedLanguage;
41 | set
42 | {
43 | if (value == selectedLanguage) return;
44 | selectedLanguage = value;
45 | raisePropertyChanged();
46 | }
47 | }
48 |
49 | public CultureInfo[] SupportedCultures => settingRoot.CultureSettings.SupportedCultures;
50 |
51 | public bool SideMenuOpen
52 | {
53 | get => sideMenuOpen;
54 | set
55 | {
56 | if(value == sideMenuOpen) return;
57 | sideMenuOpen = value;
58 | raisePropertyChanged();
59 | }
60 | }
61 |
62 | public ConnectionState ConnectionState => helper?.Value ?? ConnectionState.None;
63 |
64 | public MainWindowViewModel(IDialogCoordinator dialogCoordinator,
65 | IPresentationService presentationService,
66 | IPlcProvider provider,
67 | SettingRoot settingRoot,
68 | IViewModelFactory viewModelFactory)
69 | {
70 | this.dialogCoordinator = dialogCoordinator;
71 | this.presentationService = presentationService;
72 | this.provider = provider;
73 | this.settingRoot = settingRoot;
74 | this.viewModelFactory = viewModelFactory;
75 | }
76 | protected override void Initialize()
77 | {
78 | InitializeLocalization();
79 | SelectedLanguage = settingRoot.CultureSettings.SelectedCulture;
80 | this.WhenAnyValue(vm => vm.SelectedLanguage)
81 | .ObserveOnDispatcher()
82 | .Do(ChangeCurrentCulture)
83 | .Subscribe()
84 | .AddDisposableTo(Disposables);
85 |
86 | ChangeLanguageCmd = ReactiveCommand.CreateFromTask(ChangeCurrentCulture)
87 | .AddDisposableTo(Disposables);
88 |
89 | var plc = provider.GetHardware();
90 |
91 | helper = plc.ConnectionState.ToProperty(this, vm => vm.ConnectionState, ConnectionState.None);
92 |
93 | Logger.Debug("Main view model initialized!");
94 |
95 | SetInitialViewModel();
96 |
97 | SetupContentPresenter();
98 |
99 | SwitchToViewModel = ReactiveCommand.CreateFromTask(SwitchTo)
100 | .AddDisposableTo(Disposables);
101 |
102 | ToggleMenu = ReactiveCommand.CreateFromTask(ToggleSideMenuOpen)
103 | .AddDisposableTo(Disposables);
104 |
105 | Login = ReactiveCommand.CreateFromTask(ShowLoginDialog)
106 | .AddDisposableTo(Disposables);
107 | Logout = ReactiveCommand.CreateFromTask(ExecuteLogout)
108 | .AddDisposableTo(Disposables);
109 |
110 | PlcErrorBarViewModel = viewModelFactory.CreateViewModel();
111 | PlcErrorBarViewModel.AddDisposableTo(Disposables);
112 | }
113 |
114 | private Task ExecuteLogout()
115 | {
116 | UserService.Logout();
117 | return Task.FromResult(Unit.Default);
118 | }
119 |
120 | private async Task ShowLoginDialog()
121 | {
122 | var result = await dialogCoordinator
123 | .ShowLoginAsync(this,
124 | "Login",
125 | "Enter your credentials",
126 | new LoginDialogSettings{EnablePasswordPreview = true});
127 |
128 | if(result == null) return Unit.Default;
129 |
130 | try
131 | {
132 | UserService.Login(result.Username, result.Password);
133 | }
134 | catch (LoginFailedException e)
135 | {
136 | Logger.Error(e, "Error while login");
137 | await dialogCoordinator.ShowMessageAsync(this, "Error", e.Message, MessageDialogStyle.Affirmative,
138 | new MetroDialogSettings() { });
139 | }
140 |
141 | return Unit.Default;
142 | }
143 |
144 | private Task ToggleSideMenuOpen()
145 | {
146 | SideMenuOpen = !SideMenuOpen;
147 | return Task.FromResult(Unit.Default);
148 | }
149 |
150 | private Task ChangeCurrentCulture(string culture)
151 | {
152 | settingRoot.CultureSettings.SelectedCulture = CultureInfo.GetCultureInfo(culture);
153 | LocalizeDictionary.Instance.Culture = CultureInfo.GetCultureInfo(culture);
154 | return Task.FromResult(Unit.Default);
155 | }
156 |
157 | private void ChangeCurrentCulture(CultureInfo culture)
158 | {
159 | Logger.Debug("Setting current culture to {culture}", culture);
160 | settingRoot.CultureSettings.SelectedCulture = culture;
161 | LocalizeDictionary.Instance.Culture = culture;
162 | }
163 |
164 | private void InitializeLocalization()
165 | {
166 | LocalizeDictionary.Instance.Culture = settingRoot.CultureSettings.SelectedCulture;
167 | }
168 |
169 | private Task SwitchTo(Type type)
170 | {
171 | presentationService.SwitchActiveViewModel(type);
172 | SideMenuOpen = false;
173 | return Task.FromResult(Unit.Default);
174 | }
175 |
176 | private void SetupContentPresenter()
177 | {
178 | presentationService.ActiveViewModel
179 | .Where(vm => vm != null)
180 | .ObserveOnDispatcher()
181 | .Do(vm => ActiveViewModel = vm as ViewModelBase)
182 | .Subscribe()
183 | .AddDisposableTo(Disposables);
184 | }
185 |
186 | private void SetInitialViewModel()
187 | {
188 | presentationService.SwitchActiveViewModel();
189 | }
190 |
191 | public ViewModelBase ActiveViewModel
192 | {
193 | get => activeViewModel;
194 | set
195 | {
196 | if (value == activeViewModel) return;
197 | activeViewModel = value;
198 | raisePropertyChanged();
199 | }
200 | }
201 |
202 | public PlcErrorBarViewModel PlcErrorBarViewModel { get; set; }
203 | }
204 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/ViewModels/SettingsViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Reactive;
2 | using System.Threading.Tasks;
3 | using ReactiveUI;
4 | using WpfApp.Gui.Design;
5 | using WpfApp.Interfaces.Extensions;
6 | using WpfApp.Interfaces.Services;
7 | using WpfApp.Interfaces.Settings;
8 |
9 | namespace WpfApp.Gui.ViewModels
10 | {
11 | public class SettingsViewModel : ViewModelBase
12 | {
13 | private readonly ISettingsProvider settingsProvider;
14 | public ApplicationSetting Setting { get; }
15 |
16 | public ReactiveCommand SaveSettings { get; set; }
17 |
18 | public SettingsViewModel(ApplicationSetting setting, ISettingsProvider settingsProvider)
19 | {
20 | this.settingsProvider = settingsProvider;
21 | Setting = setting;
22 | }
23 | protected override void Initialize()
24 | {
25 | SaveSettings = ReactiveCommand.CreateFromTask(Save).AddDisposableTo(Disposables);
26 | }
27 |
28 | private Task Save()
29 | {
30 | settingsProvider.SaveSettings();
31 | return Task.FromResult(Unit.Default);
32 | }
33 | }
34 |
35 | internal class DesignSettingsViewModel : SettingsViewModel
36 | {
37 | public DesignSettingsViewModel() : base(new ApplicationSetting(), new DesignSettingsProvider())
38 | {
39 | Init();
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/ViewModels/ViewModelBase.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using System.Runtime.CompilerServices;
6 | using JetBrains.Annotations;
7 | using Ninject;
8 | using ReactiveUI;
9 | using Serilog;
10 | using WpfApp.Interfaces.Enums;
11 | using WpfApp.Interfaces.Extensions;
12 | using WpfApp.Interfaces.Models;
13 | using WpfApp.Interfaces.Services;
14 | using WpfApp.Interfaces.Ui;
15 |
16 | namespace WpfApp.Gui.ViewModels
17 | {
18 | public abstract class ViewModelBase : ReactiveObject, IViewModel
19 | {
20 | protected CompositeDisposable Disposables = new CompositeDisposable();
21 | private bool disposed;
22 | private string title;
23 | private ObservableAsPropertyHelper currentUserHelper;
24 | private ObservableAsPropertyHelper loggedInHelper;
25 | private ObservableAsPropertyHelper> rolesInHelper;
26 |
27 | [Inject]
28 | public IUserService UserService { get; set; }
29 |
30 | public string Title
31 | {
32 | get => title;
33 | set
34 | {
35 | if (value == title) return;
36 | title = value;
37 | raisePropertyChanged();
38 | }
39 | }
40 |
41 | protected ILogger Logger { get; } = Log.Logger;
42 |
43 | public virtual void Dispose()
44 | {
45 | Dispose(true);
46 | }
47 |
48 | public void Init()
49 | {
50 | Initialize();
51 | InitializeBaseElements();
52 | }
53 |
54 | protected abstract void Initialize();
55 |
56 | [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "Disposables")]
57 | protected virtual void Dispose(bool disposing)
58 | {
59 | if (disposed)
60 | {
61 | return;
62 | }
63 |
64 | Disposables?.Dispose();
65 | Disposables = null;
66 | Logger?.Debug("Disposed {type}", this.GetType().Name);
67 |
68 | disposed = true;
69 | }
70 |
71 | [NotifyPropertyChangedInvocator]
72 | // ReSharper disable once InconsistentNaming
73 | protected void raisePropertyChanged([CallerMemberName] string propertyName = "")
74 | {
75 | this.RaisePropertyChanged(propertyName);
76 | }
77 |
78 | ~ViewModelBase()
79 | {
80 | Dispose(false);
81 | }
82 |
83 | private void InitializeBaseElements()
84 | {
85 | currentUserHelper = UserService?.CurrentUser.ToProperty(this, vm => vm.CurrentUser);
86 | currentUserHelper.AddDisposableTo(Disposables);
87 |
88 | loggedInHelper = UserService?.CurrentUser.Select(u => u != null).ToProperty(this, vm => vm.LoggedIn);
89 | loggedInHelper.AddDisposableTo(Disposables);
90 |
91 | rolesInHelper = UserService?.CurrentUser.Select(u => u?.Roles ?? new List()).ToProperty(this, vm => vm.CurrentRoles);
92 | rolesInHelper.AddDisposableTo(Disposables);
93 | }
94 |
95 | public List CurrentRoles => rolesInHelper.Value;
96 |
97 | public bool LoggedIn => loggedInHelper.Value;
98 |
99 | public User CurrentUser => currentUserHelper.Value;
100 | }
101 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcButton.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
17 |
18 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcButton.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace WpfApp.Gui.Views.Basics
2 | {
3 | public partial class PlcButton : PlcUserControl
4 | {
5 | public PlcButton()
6 | {
7 | InitializeComponent();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcErrorBar.xaml:
--------------------------------------------------------------------------------
1 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcErrorBar.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace WpfApp.Gui.Views.Basics
4 | {
5 | public partial class PlcErrorBar : UserControl
6 | {
7 | public PlcErrorBar()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcErrorDetails.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcErrorDetails.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace WpfApp.Gui.Views.Basics
4 | {
5 | public partial class PlcErrorDetails : UserControl
6 | {
7 | public PlcErrorDetails()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcSignalOkNok.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcSignalOkNok.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace WpfApp.Gui.Views.Basics
2 | {
3 | public partial class PlcSignalOkNok : PlcUserControl
4 | {
5 | public PlcSignalOkNok()
6 | {
7 | InitializeComponent();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcSignalOnOff.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcSignalOnOff.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace WpfApp.Gui.Views.Basics
2 | {
3 | public partial class PlcSignalOnOff : PlcUserControl
4 | {
5 | public PlcSignalOnOff()
6 | {
7 | InitializeComponent();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcUserControl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Disposables;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using ReactiveUI;
6 | using WpfApp.Gui.ViewModels;
7 | using WpfApp.Gui.ViewModels.Basics;
8 |
9 | namespace WpfApp.Gui.Views.Basics
10 | {
11 | public class PlcUserControl : UserControl, IViewFor
12 | {
13 | public PlcUserControl()
14 | {
15 | this.WhenActivated(disposables =>
16 | {
17 | if (ViewModel != null)
18 | {
19 | ViewModel.SetupVariableViewModel(PlcName, VariablePath, Label, Description, SetParameter, ValueFormat);
20 | ViewModel.DisposeWith(disposables);
21 | }
22 | });
23 | }
24 |
25 | public readonly DependencyProperty VariablePathProperty = DependencyProperty.Register(nameof(VariablePath)+Guid.NewGuid(), typeof(string), typeof(PlcUserControl));
26 | public readonly DependencyProperty SetParameterProperty = DependencyProperty.Register(nameof(SetParameter)+Guid.NewGuid(), typeof(object), typeof(PlcUserControl));
27 | public readonly DependencyProperty ValueFormatProperty = DependencyProperty.Register(nameof(ValueFormat)+Guid.NewGuid(), typeof(string), typeof(PlcUserControl));
28 | public readonly DependencyProperty PlcNameProperty = DependencyProperty.Register(nameof(PlcNameProperty)+Guid.NewGuid(), typeof(string), typeof(PlcUserControl));
29 | public readonly DependencyProperty LabelProperty = DependencyProperty.Register(nameof(LabelProperty)+Guid.NewGuid(), typeof(string), typeof(PlcUserControl));
30 | public readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(nameof(DescriptionProperty)+Guid.NewGuid(), typeof(string), typeof(PlcUserControl));
31 |
32 | public string VariablePath
33 | {
34 | get => (string) GetValue(VariablePathProperty);
35 | set => SetValue(VariablePathProperty, value);
36 | }
37 |
38 | public object SetParameter
39 | {
40 | get => GetValue(SetParameterProperty);
41 | set => SetValue(SetParameterProperty, value);
42 | }
43 |
44 | public string ValueFormat
45 | {
46 | get => (string) GetValue(ValueFormatProperty);
47 | set => SetValue(ValueFormatProperty, value);
48 | }
49 |
50 | public string PlcName
51 | {
52 | get => (string) GetValue(PlcNameProperty);
53 | set => SetValue(PlcNameProperty, value);
54 | }
55 | public string Label
56 | {
57 | get => (string) GetValue(LabelProperty);
58 | set => SetValue(LabelProperty, value);
59 | }
60 | public string Description
61 | {
62 | get => (string) GetValue(DescriptionProperty);
63 | set => SetValue(DescriptionProperty, value);
64 | }
65 |
66 | object? IViewFor.ViewModel
67 | {
68 | get => ViewModel;
69 | set => ViewModel = value as PlcVariableViewModel;
70 | }
71 |
72 | public PlcVariableViewModel? ViewModel
73 | {
74 | get => DataContext as PlcVariableViewModel;
75 | set => DataContext = value;
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcVariable.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
31 |
32 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Basics/PlcVariable.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace WpfApp.Gui.Views.Basics
2 | {
3 | public partial class PlcVariable : PlcUserControl
4 | {
5 | public PlcVariable()
6 | {
7 | InitializeComponent();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
57 |
58 |
63 |
64 |
65 |
66 |
80 |
81 |
87 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
118 |
119 |
120 |
127 |
131 |
132 |
133 |
137 |
140 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using ControlzEx.Theming;
3 | using MahApps.Metro.Controls;
4 |
5 | namespace WpfApp.Gui.Views
6 | {
7 | ///
8 | /// Interaction logic for MainWindow.xaml
9 | ///
10 | public partial class MainWindow : MetroWindow
11 | {
12 | public MainWindow()
13 | {
14 | InitializeComponent();
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Pages/GraphView.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Pages/GraphView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace WpfApp.Gui.Views.Pages
4 | {
5 | public partial class GraphView : UserControl
6 | {
7 | public GraphView()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Pages/MainView.xaml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Pages/MainView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace WpfApp.Gui.Views.Pages
4 | {
5 | public partial class MainView : UserControl
6 | {
7 | public MainView()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Pages/SettingsView.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/WpfApp.Gui/Views/Pages/SettingsView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace WpfApp.Gui.Views.Pages
4 | {
5 | public partial class SettingsView : UserControl
6 | {
7 | public SettingsView()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/WpfApp.Gui/WpfApp.Gui.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | MSBuild:Compile
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ResXFileCodeGenerator
44 | Contents.Designer.cs
45 |
46 |
47 | ResXFileCodeGenerator
48 | Contents.de.Designer.cs
49 |
50 |
51 |
52 |
53 |
54 | True
55 | True
56 | Contents.resx
57 |
58 |
59 | True
60 | Contents.de.resx
61 | True
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Commons/IInitializable.cs:
--------------------------------------------------------------------------------
1 | namespace WpfApp.Interfaces.Commons
2 | {
3 | public interface IInitializable
4 | {
5 | void Init();
6 | }
7 | }
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Commons/IInstanceCreator.cs:
--------------------------------------------------------------------------------
1 | using Ninject.Parameters;
2 |
3 | namespace WpfApp.Interfaces.Commons
4 | {
5 | public interface IInstanceCreator
6 | {
7 | T CreateInstance(ConstructorArgument[] arguments);
8 | }
9 | }
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Commons/IViewModelFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using WpfApp.Interfaces.Ui;
3 |
4 | namespace WpfApp.Interfaces.Commons
5 | {
6 | public interface IViewModelFactory
7 | {
8 | T Create();
9 |
10 | TVm CreateViewModel(T model);
11 |
12 | TVm CreateViewModel();
13 | IViewModel CreateViewModel(Type viewModelType);
14 | }
15 | }
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Constants.cs:
--------------------------------------------------------------------------------
1 | namespace WpfApp.Interfaces
2 | {
3 | public class Constants
4 | {
5 | public static string ApplicationName => "WpfApp";
6 | public static string LoggerDirectory => "Logs";
7 | public static string DatabaseDirectory => "Data";
8 | public static string SettingsDirectory => "Settings";
9 | }
10 | }
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Enums/Role.cs:
--------------------------------------------------------------------------------
1 | namespace WpfApp.Interfaces.Enums
2 | {
3 | public enum Role
4 | {
5 | Root = 0,
6 | Technician = 10,
7 | Service = 20,
8 | Operator = 30
9 | }
10 | }
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Enums/Severity.cs:
--------------------------------------------------------------------------------
1 | namespace WpfApp.Interfaces.Enums
2 | {
3 | public enum Severity
4 | {
5 | Fatal,
6 | Critical,
7 | Error,
8 | Warning,
9 | Info,
10 | Debug,
11 | Ignored,
12 | NoError
13 | }
14 | }
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Exceptions/LoginFailedException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WpfApp.Interfaces.Exceptions
4 | {
5 | public class LoginFailedException : Exception
6 | {
7 | public LoginFailedException(string message): base(message)
8 | {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Extensions/DisposableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Disposables;
3 |
4 | namespace WpfApp.Interfaces.Extensions
5 | {
6 | public static class DisposableExtensions
7 | {
8 | public static T AddDisposableTo(this T source, CompositeDisposable disposables) where T : IDisposable
9 | {
10 | disposables.Add((IDisposable) source);
11 | return source;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Extensions/RandomExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WpfApp.Interfaces.Extensions
4 | {
5 | public static class RandomExtensions
6 | {
7 | public static T GetData(this Random random)
8 | {
9 | var type = typeof(T);
10 | switch (Type.GetTypeCode(type))
11 | {
12 | case TypeCode.Boolean:
13 | return (T)Convert.ChangeType(random.Next() % 2 == 0, type);
14 | case TypeCode.Char:
15 | case TypeCode.SByte:
16 | case TypeCode.Byte:
17 | case TypeCode.Int16:
18 | case TypeCode.UInt16:
19 | case TypeCode.Int32:
20 | case TypeCode.UInt32:
21 | case TypeCode.Int64:
22 | case TypeCode.UInt64:
23 | return (T)Convert.ChangeType(random.Next(), type);
24 | case TypeCode.Single:
25 | case TypeCode.Double:
26 | case TypeCode.Decimal:
27 | return (T)Convert.ChangeType(random.NextDouble(), type);
28 | case TypeCode.DateTime:
29 | return (T)Convert.ChangeType(DateTime.Now, type);
30 | case TypeCode.String:
31 | return (T)Convert.ChangeType(Guid.NewGuid().ToString(), type);
32 | default:
33 | return default(T);
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/WpfApp.Interfaces/Hardware/IPlc.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using TwinCAT;
4 |
5 | namespace WpfApp.Interfaces.Hardware
6 | {
7 | public interface IPlc : IDisposable
8 | {
9 | public IObservable ConnectionState { get; }
10 | public IObservable CreateNotification(string variable);
11 | public IObservable CreateNotification(string variable, TimeSpan cycle);
12 | public Task Read(string variable);
13 | public Task Write(string variable, T value);
14 | IObservable