forked from PLCnext/CSharpExamples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DCG_example3.cs
216 lines (198 loc) · 6.16 KB
/
DCG_example3.cs
1
2
3
4
5
6
7
8
9
10
11
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#region Copyright
//
// Copyright (c) Phoenix Contact GmbH & Co. KG. All rights reserved.
// Licensed under the MIT. See LICENSE file in the project root for full license information.
//
#endregion
using System;
using System.Iec61131Lib;
using System.Collections.Generic;
using Eclr;
using System.Threading;
using Eclr.Pcos;
using Iec61131.Engineering.Prototypes.Common;
using Iec61131.Engineering.Prototypes.Methods;
using Iec61131.Engineering.Prototypes.Types;
using Iec61131.Engineering.Prototypes.Variables;
// This pattern represents the use case that code of a FB should be executed in a background task.
// The background task executes all FBs that exist in a container.
// Compared to the example of DcgBestPracticePattern3 in this pattern the FB remains in the container until its job is done. Then it is removed from it.
// The advantage of this solution is that no implementation layer of the function block is needed (as shown in DcgBestPracticePattern3).
namespace DcgBestPracticePattern5
{
[FunctionBlock]
public class BackgroundDemoFb3 : BackgroundFb
{
[Input, DataType("BOOL")]
public bool Execute;
[Output, DataType("DINT")]
public int Value;
private DateTime dueTime;
#region Rising edge detection
private bool lastExecute;
private bool IsExecute
{
get
{
if (Execute && !lastExecute)
{
lastExecute = Execute;
return true;
}
lastExecute = Execute;
return false;
}
}
#endregion
public BackgroundDemoFb3()
{
}
~BackgroundDemoFb3()
{
}
[Initialization]
public void __Init()
{
}
[Execution]
public void __Process()
{
if(IsExecute && (dueTime < DateTime.Now))
{
dueTime = DateTime.Now.AddSeconds(1); // Do a job for 1 second
WorkerThread.AddJob(this);
}
}
public override void Exec()
{
Value++;
if (DateTime.Now < dueTime)
{
// If the FB has still something to do add a new job to the worker thread
WorkerThread.AddJob(this);
}
}
}
// Base class for background FBs
public class BackgroundFb
{
internal BackgroundFb next;
internal BackgroundFb()
{
// Worker thread is designed to create just one thread per AppDomain although called more than once.
WorkerThread.CreateThread();
}
public virtual void Exec() { }
}
#region Worker Thread
/// <summary>
/// Class creating a thread (that must be static)
/// </summary>
class WorkerThread
{
private static Thread worker;
private static BackgroundFb top;
internal static bool AddJob(BackgroundFb fb)
{
bool result = false;
if (Resource.Running)
{
if (fb != null)
{
fb.Exec();
result = true;
}
}
else
{
lock (worker)
{
if (top == null)
{
top = fb;
}
else
{
bool bBreak = false;
BackgroundFb iter = top;
while (iter.next != null)
{
if (iter.next == fb)
{
// an object is allowed to exist only once in the queue
bBreak = true;
break;
}
iter = iter.next;
}
if (!bBreak)
{
if (iter != fb) // avoid recursion in chain (important if top.next == null)
{
iter.next = fb;
result = true;
}
}
}
}
}
return result;
}
internal static bool CreateThread()
{
if (worker == null)
{
ThreadStart start = new ThreadStart(TaskBody);
worker = new Thread(start)
{
Priority = ThreadPriority.Lowest
};
worker.Start();
return true;
}
return false;
}
private static void TaskBody()
{
while (true)
{
// Just execute jobs if the PLC state is Running
if (Resource.Running)
{
Exec();
}
Thread.Sleep(10);
}
}
private static void Exec()
{
if (top != null)
{
// determine amount of jobs
BackgroundFb next = top.next;
int amount = 1;
while (next != null)
{
amount++;
next = next.next;
}
// execute this amount of jobs
// if jobs are added during execution of this jobs then
// these jobs are executed in a next cycle
while (top != null && amount != 0)
{
BackgroundFb current = top;
lock (worker)
{
BackgroundFb temp = top; // remember top object to set top.next to null
top = top.next; // next job
temp.next = null;
}
amount--;
current.Exec(); // execute job
}
}
}
}
#endregion
}