nodalis-compiler 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -0
- package/package.json +59 -0
- package/src/compilers/CPPCompiler.js +272 -0
- package/src/compilers/Compiler.js +108 -0
- package/src/compilers/JSCompiler.js +293 -0
- package/src/compilers/iec-parser/parser.js +4254 -0
- package/src/compilers/st-parser/expressionConverter.js +155 -0
- package/src/compilers/st-parser/gcctranspiler.js +237 -0
- package/src/compilers/st-parser/jstranspiler.js +254 -0
- package/src/compilers/st-parser/parser.js +367 -0
- package/src/compilers/st-parser/tokenizer.js +78 -0
- package/src/compilers/support/generic/json.hpp +25526 -0
- package/src/compilers/support/generic/modbus.cpp +378 -0
- package/src/compilers/support/generic/modbus.h +124 -0
- package/src/compilers/support/generic/nodalis.cpp +421 -0
- package/src/compilers/support/generic/nodalis.h +798 -0
- package/src/compilers/support/generic/opcua.cpp +267 -0
- package/src/compilers/support/generic/opcua.h +50 -0
- package/src/compilers/support/generic/open62541.c +151897 -0
- package/src/compilers/support/generic/open62541.h +50357 -0
- package/src/compilers/support/jint/nodalis/Nodalis.sln +28 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/ModbusClient.cs +200 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/NodalisEngine.cs +817 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/NodalisEngine.csproj +16 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/OPCClient.cs +172 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/OPCServer.cs +275 -0
- package/src/compilers/support/jint/nodalis/NodalisPLC/NodalisPLC.csproj +19 -0
- package/src/compilers/support/jint/nodalis/NodalisPLC/Program.cs +197 -0
- package/src/compilers/support/jint/nodalis/NodalisPLC/bootstrap.bat +5 -0
- package/src/compilers/support/jint/nodalis/NodalisPLC/bootstrap.sh +5 -0
- package/src/compilers/support/jint/nodalis/build.bat +25 -0
- package/src/compilers/support/jint/nodalis/build.sh +31 -0
- package/src/compilers/support/nodejs/IOClient.js +110 -0
- package/src/compilers/support/nodejs/modbus.js +115 -0
- package/src/compilers/support/nodejs/nodalis.js +662 -0
- package/src/compilers/support/nodejs/opcua.js +194 -0
- package/src/nodalis.js +174 -0
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
#nullable enable
|
|
2
|
+
|
|
3
|
+
// Copyright [2025] Nathan Skipper
|
|
4
|
+
//
|
|
5
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
// you may not use this file except in compliance with the License.
|
|
7
|
+
// You may obtain a copy of the License at
|
|
8
|
+
//
|
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
//
|
|
11
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
// See the License for the specific language governing permissions and
|
|
15
|
+
// limitations under the License.
|
|
16
|
+
|
|
17
|
+
/// <summary>
|
|
18
|
+
/// Nodalis Framework for .NET
|
|
19
|
+
/// </summary>
|
|
20
|
+
|
|
21
|
+
using System;
|
|
22
|
+
using System.Collections.Generic;
|
|
23
|
+
using System.Text.Json;
|
|
24
|
+
using Jint;
|
|
25
|
+
using Jint.Native;
|
|
26
|
+
using Jint.Native.Object;
|
|
27
|
+
using Jint.Native.Function;
|
|
28
|
+
using Jint.Runtime.Interop;
|
|
29
|
+
using Jint.Runtime;
|
|
30
|
+
namespace Nodalis
|
|
31
|
+
{
|
|
32
|
+
public class StaticStore
|
|
33
|
+
{
|
|
34
|
+
private readonly Dictionary<string, JsValue> _store = new();
|
|
35
|
+
|
|
36
|
+
public bool TryGetValue(string key, out JsValue value) =>
|
|
37
|
+
_store.TryGetValue(key, out value);
|
|
38
|
+
|
|
39
|
+
public JsValue this[string key]
|
|
40
|
+
{
|
|
41
|
+
get => _store[key];
|
|
42
|
+
set => _store[key] = value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
public enum IOType { Input, Output }
|
|
48
|
+
|
|
49
|
+
public class IOMap
|
|
50
|
+
{
|
|
51
|
+
public string localAddress;
|
|
52
|
+
public string remoteAddress;
|
|
53
|
+
public string moduleID;
|
|
54
|
+
public string modulePort;
|
|
55
|
+
public string protocol;
|
|
56
|
+
public IOType direction;
|
|
57
|
+
public int width;
|
|
58
|
+
public int interval;
|
|
59
|
+
public long lastPoll = 0;
|
|
60
|
+
public Dictionary<string, string>? protocolProperties;
|
|
61
|
+
public IOMap(string json)
|
|
62
|
+
{
|
|
63
|
+
var dict = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(json);
|
|
64
|
+
localAddress = dict["InternalAddress"];
|
|
65
|
+
remoteAddress = dict["RemoteAddress"];
|
|
66
|
+
moduleID = dict["ModuleID"];
|
|
67
|
+
modulePort = dict["ModulePort"];
|
|
68
|
+
protocol = dict["Protocol"];
|
|
69
|
+
direction = localAddress.StartsWith("%Q") ? IOType.Output : IOType.Input;
|
|
70
|
+
width = int.Parse(dict["RemoteSize"]);
|
|
71
|
+
interval = int.Parse(dict["PollTime"]);
|
|
72
|
+
if (dict.TryGetValue("ProtocolProperties", out var nestedJson))
|
|
73
|
+
{
|
|
74
|
+
try
|
|
75
|
+
{
|
|
76
|
+
protocolProperties = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(nestedJson);
|
|
77
|
+
}
|
|
78
|
+
catch
|
|
79
|
+
{
|
|
80
|
+
protocolProperties = null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public abstract class IOClient
|
|
87
|
+
{
|
|
88
|
+
public bool connected = false;
|
|
89
|
+
public string protocol;
|
|
90
|
+
public string moduleID;
|
|
91
|
+
protected List<IOMap> mappings = new();
|
|
92
|
+
protected long lastAttempt = 0;
|
|
93
|
+
|
|
94
|
+
protected IOClient(string protocol)
|
|
95
|
+
{
|
|
96
|
+
this.protocol = protocol;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public void AddMapping(IOMap map)
|
|
100
|
+
{
|
|
101
|
+
if (!mappings.Exists(m => m.localAddress == map.localAddress))
|
|
102
|
+
{
|
|
103
|
+
if (mappings.Count == 0)
|
|
104
|
+
{
|
|
105
|
+
moduleID = map.moduleID;
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
mappings.Add(map);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public bool HasMapping(string localAddress) => mappings.Exists(m => m.localAddress == localAddress);
|
|
113
|
+
|
|
114
|
+
public void Poll(NodalisEngine engine)
|
|
115
|
+
{
|
|
116
|
+
foreach (var map in mappings)
|
|
117
|
+
{
|
|
118
|
+
if (engine.ElapsedMilliseconds - map.lastPoll >= map.interval)
|
|
119
|
+
{
|
|
120
|
+
map.lastPoll = engine.ElapsedMilliseconds;
|
|
121
|
+
try
|
|
122
|
+
{
|
|
123
|
+
if (map.direction == IOType.Output)
|
|
124
|
+
{
|
|
125
|
+
switch (map.width)
|
|
126
|
+
{
|
|
127
|
+
case 1: WriteBit(map.remoteAddress, engine.ReadBit(map.localAddress) ? 1 : 0); break;
|
|
128
|
+
case 8: WriteByte(map.remoteAddress, engine.ReadByte(map.localAddress)); break;
|
|
129
|
+
case 16: WriteWord(map.remoteAddress, engine.ReadWord(map.localAddress)); break;
|
|
130
|
+
case 32: WriteDWord(map.remoteAddress, engine.ReadDWord(map.localAddress)); break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else
|
|
134
|
+
{
|
|
135
|
+
switch (map.width)
|
|
136
|
+
{
|
|
137
|
+
case 1: if (ReadBit(map.remoteAddress, out var bit)) engine.WriteBit(map.localAddress, bit != 0); break;
|
|
138
|
+
case 8: if (ReadByte(map.remoteAddress, out var b)) engine.WriteByte(map.localAddress, b); break;
|
|
139
|
+
case 16: if (ReadWord(map.remoteAddress, out var w)) engine.WriteWord(map.localAddress, w); break;
|
|
140
|
+
case 32: if (ReadDWord(map.remoteAddress, out var d)) engine.WriteDWord(map.localAddress, d); break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch { }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public abstract void Connect();
|
|
150
|
+
public abstract bool ReadBit(string address, out int result);
|
|
151
|
+
public abstract bool WriteBit(string address, int value);
|
|
152
|
+
public abstract bool ReadByte(string address, out byte result);
|
|
153
|
+
public abstract bool WriteByte(string address, byte value);
|
|
154
|
+
public abstract bool ReadWord(string address, out ushort result);
|
|
155
|
+
public abstract bool WriteWord(string address, ushort value);
|
|
156
|
+
public abstract bool ReadDWord(string address, out uint result);
|
|
157
|
+
public abstract bool WriteDWord(string address, uint value);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public class FunctionBlock
|
|
161
|
+
{
|
|
162
|
+
private readonly ObjectInstance _jsObj;
|
|
163
|
+
private readonly Engine _engine;
|
|
164
|
+
|
|
165
|
+
public FunctionBlock(Engine engine, string className)
|
|
166
|
+
{
|
|
167
|
+
_engine = engine;
|
|
168
|
+
var constructor = engine.GetValue(className);
|
|
169
|
+
_jsObj = engine.Invoke(constructor).AsObject();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
public void Set(string name, object value) =>
|
|
173
|
+
_jsObj.Set(name, JsValue.FromObject(_engine, value), throwOnError: true);
|
|
174
|
+
|
|
175
|
+
public T Get<T>(string name) =>
|
|
176
|
+
_jsObj.Get(name).ToObject() is T t ? t : default;
|
|
177
|
+
|
|
178
|
+
public void Call()
|
|
179
|
+
{
|
|
180
|
+
var callFn = _jsObj.Get("call");
|
|
181
|
+
_engine.Invoke(callFn, _jsObj);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
public abstract partial class NodalisEngine
|
|
187
|
+
{
|
|
188
|
+
private readonly StaticStore _staticStore = new();
|
|
189
|
+
protected readonly Engine JsEngine = new Engine(cfg => { cfg.AllowClr(); });
|
|
190
|
+
private readonly List<IOClient> Clients = new();
|
|
191
|
+
private readonly DateTime StartTime = DateTime.UtcNow;
|
|
192
|
+
|
|
193
|
+
public long ElapsedMilliseconds => (long)(DateTime.UtcNow - StartTime).TotalMilliseconds;
|
|
194
|
+
|
|
195
|
+
public NodalisEngine()
|
|
196
|
+
{
|
|
197
|
+
InjectBindings();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public void Load(string code) => JsEngine.Execute(code);
|
|
201
|
+
public void Setup() => JsEngine.Invoke("setup");
|
|
202
|
+
public void Execute() => JsEngine.Invoke("run");
|
|
203
|
+
public FunctionBlock CreateFunctionBlock(string name)
|
|
204
|
+
{
|
|
205
|
+
return new FunctionBlock(JsEngine, name);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
public void MapIO(string json)
|
|
209
|
+
{
|
|
210
|
+
try
|
|
211
|
+
{
|
|
212
|
+
var map = new IOMap(json);
|
|
213
|
+
var client = Clients.Find(c => c.HasMapping(map.localAddress) || c.moduleID == map.moduleID);
|
|
214
|
+
if (client == null)
|
|
215
|
+
{
|
|
216
|
+
client = CreateClient(map);
|
|
217
|
+
if (client != null)
|
|
218
|
+
{
|
|
219
|
+
client.Connect();
|
|
220
|
+
Clients.Add(client);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else client.AddMapping(map);
|
|
224
|
+
}
|
|
225
|
+
catch (Exception ex) { Console.WriteLine($"mapIO error: {ex.Message}"); }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
public void SuperviseIO()
|
|
229
|
+
{
|
|
230
|
+
foreach (var client in Clients)
|
|
231
|
+
{
|
|
232
|
+
client.Poll(this);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
protected virtual IOClient? CreateClient(IOMap map)
|
|
237
|
+
{
|
|
238
|
+
IOClient client = null;
|
|
239
|
+
if (map.protocol == "MODBUS-TCP")
|
|
240
|
+
client = new ModbusClient();
|
|
241
|
+
else if (map.protocol == "OPCUA")
|
|
242
|
+
client = new OPCClient();
|
|
243
|
+
if (client != null)
|
|
244
|
+
{
|
|
245
|
+
client.AddMapping(map);
|
|
246
|
+
}
|
|
247
|
+
return client;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public static bool GetBit(uint value, int bit)
|
|
251
|
+
{
|
|
252
|
+
return (value & (1u << bit)) != 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
public static void SetBit(ref uint value, int bit, bool state)
|
|
256
|
+
{
|
|
257
|
+
value = state ? (value | (1u << bit)) : (value & ~(1u << bit));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public static bool GetBit<T>(RefVar<T> var, int bit) where T : struct
|
|
261
|
+
{
|
|
262
|
+
uint val = Convert.ToUInt32(var.Value);
|
|
263
|
+
return (val & (1u << bit)) != 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
public static void SetBit<T>(ref RefVar<T> var, int bit, bool state) where T : struct
|
|
267
|
+
{
|
|
268
|
+
uint val = Convert.ToUInt32(var.Value);
|
|
269
|
+
val = state ? (val | (1u << bit)) : (val & ~(1u << bit));
|
|
270
|
+
var.Value = (T)Convert.ChangeType(val, typeof(T));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
public abstract bool ReadBit(string address);
|
|
274
|
+
public abstract void WriteBit(string address, bool value);
|
|
275
|
+
public abstract byte ReadByte(string address);
|
|
276
|
+
public abstract void WriteByte(string address, byte value);
|
|
277
|
+
public abstract ushort ReadWord(string address);
|
|
278
|
+
public abstract void WriteWord(string address, ushort value);
|
|
279
|
+
public abstract uint ReadDWord(string address);
|
|
280
|
+
public abstract void WriteDWord(string address, uint value);
|
|
281
|
+
|
|
282
|
+
public object CreateReference(string address)
|
|
283
|
+
{
|
|
284
|
+
address = address.ToUpperInvariant();
|
|
285
|
+
|
|
286
|
+
Type type = typeof(bool);
|
|
287
|
+
if (address.Contains(".")) type = typeof(bool);
|
|
288
|
+
else if (address.Contains("W")) type = typeof(ushort);
|
|
289
|
+
else if (address.Contains("D")) type = typeof(uint);
|
|
290
|
+
else if (address.Contains("X")) type = typeof(byte);
|
|
291
|
+
|
|
292
|
+
var refVarType = typeof(RefVar<>).MakeGenericType(type);
|
|
293
|
+
var instance = Activator.CreateInstance(refVarType, this, address);
|
|
294
|
+
return instance;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private void InjectBindings()
|
|
298
|
+
{
|
|
299
|
+
JsEngine.SetValue("readBit", new Func<string, bool>(ReadBit));
|
|
300
|
+
JsEngine.SetValue("writeBit", new Action<string, bool>(WriteBit));
|
|
301
|
+
JsEngine.SetValue("readByte", new Func<string, byte>(ReadByte));
|
|
302
|
+
JsEngine.SetValue("writeByte", new Action<string, byte>(WriteByte));
|
|
303
|
+
JsEngine.SetValue("readWord", new Func<string, ushort>(ReadWord));
|
|
304
|
+
JsEngine.SetValue("writeWord", new Action<string, ushort>(WriteWord));
|
|
305
|
+
JsEngine.SetValue("readDWord", new Func<string, uint>(ReadDWord));
|
|
306
|
+
JsEngine.SetValue("writeDWord", new Action<string, uint>(WriteDWord));
|
|
307
|
+
JsEngine.SetValue("elapsed", new Func<long>(() => ElapsedMilliseconds));
|
|
308
|
+
JsEngine.SetValue("mapIO", new Action<string>(MapIO));
|
|
309
|
+
JsEngine.SetValue("superviseIO", new Action(SuperviseIO));
|
|
310
|
+
JsEngine.SetValue("writeDWord", new Action<string, uint>(WriteDWord));
|
|
311
|
+
JsEngine.SetValue("log", new Action<string>(Console.WriteLine));
|
|
312
|
+
JsEngine.SetValue("error", new Action<string>(Console.Error.WriteLine));
|
|
313
|
+
|
|
314
|
+
JsEngine.SetValue("getBit", new ClrFunctionInstance(JsEngine, "getBit", (thisObj, args) =>
|
|
315
|
+
{
|
|
316
|
+
if (args[0].IsObject())
|
|
317
|
+
{
|
|
318
|
+
var jsObj = args[0].AsObject();
|
|
319
|
+
var refObj = jsObj.ToObject();
|
|
320
|
+
if (refObj != null && refObj.GetType().IsGenericType && refObj.GetType().GetGenericTypeDefinition() == typeof(RefVar<>))
|
|
321
|
+
{
|
|
322
|
+
|
|
323
|
+
var bit = (int)args[1].AsNumber();
|
|
324
|
+
var method = typeof(NodalisEngine).GetMethod("GetBit").MakeGenericMethod(refObj.GetType().GetGenericArguments()[0]);
|
|
325
|
+
return (bool)method.Invoke(null, new[] { refObj, bit });
|
|
326
|
+
}
|
|
327
|
+
else
|
|
328
|
+
{
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else
|
|
333
|
+
{
|
|
334
|
+
var value = (uint)args[0].AsNumber();
|
|
335
|
+
var bit = (int)args[1].AsNumber();
|
|
336
|
+
return GetBit(value, bit);
|
|
337
|
+
}
|
|
338
|
+
}));
|
|
339
|
+
|
|
340
|
+
JsEngine.SetValue("setBit", new ClrFunctionInstance(JsEngine, "setBit", (thisObj, args) =>
|
|
341
|
+
{
|
|
342
|
+
if (args[0].ToObject() is RefVar<bool> refVar)
|
|
343
|
+
{
|
|
344
|
+
var jsObj = args[0].AsObject();
|
|
345
|
+
var refObj = jsObj.ToObject();
|
|
346
|
+
var bit = (int)args[1].AsNumber();
|
|
347
|
+
var state = args[2].AsBoolean();
|
|
348
|
+
var method = typeof(NodalisEngine).GetMethod("SetBit", new[] { refObj.GetType(), typeof(int), typeof(bool) });
|
|
349
|
+
if (method != null)
|
|
350
|
+
{
|
|
351
|
+
var parameters = new object[] { refObj, bit, state };
|
|
352
|
+
method.Invoke(null, parameters);
|
|
353
|
+
}
|
|
354
|
+
return JsValue.Undefined;
|
|
355
|
+
}
|
|
356
|
+
else
|
|
357
|
+
{
|
|
358
|
+
var value = (uint)args[0].AsNumber();
|
|
359
|
+
var bit = (int)args[1].AsNumber();
|
|
360
|
+
var state = args[2].AsBoolean();
|
|
361
|
+
SetBit(ref value, bit, state);
|
|
362
|
+
return JsValue.FromObject(JsEngine, value);
|
|
363
|
+
}
|
|
364
|
+
}));
|
|
365
|
+
|
|
366
|
+
JsEngine.SetValue("createReference", new Func<string, object>(CreateReference));
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
JsEngine.SetValue("newStatic", new ClrFunctionInstance(JsEngine, "newStatic", (thisObj, args) =>
|
|
370
|
+
{
|
|
371
|
+
var key = args[0].AsString();
|
|
372
|
+
var jsCtor = args[1];
|
|
373
|
+
|
|
374
|
+
if (!_staticStore.TryGetValue(key, out var instance))
|
|
375
|
+
{
|
|
376
|
+
var jsInstance = JsEngine.Invoke(jsCtor);
|
|
377
|
+
_staticStore[key] = jsInstance;
|
|
378
|
+
return jsInstance;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return (JsValue)_staticStore[key];
|
|
382
|
+
}));
|
|
383
|
+
|
|
384
|
+
JsEngine.SetValue("resolve", new ClrFunctionInstance(JsEngine, "resolve", (thisObj, args) =>
|
|
385
|
+
{
|
|
386
|
+
var val = args[0].ToObject();
|
|
387
|
+
if (val != null && val.GetType().IsGenericType &&
|
|
388
|
+
val.GetType().GetGenericTypeDefinition() == typeof(RefVar<>))
|
|
389
|
+
{
|
|
390
|
+
var valueProp = val.GetType().GetProperty("Value");
|
|
391
|
+
return JsValue.FromObject(JsEngine, valueProp.GetValue(val));
|
|
392
|
+
}
|
|
393
|
+
return args[0];
|
|
394
|
+
}));
|
|
395
|
+
|
|
396
|
+
var types = new[] {
|
|
397
|
+
typeof(TON), typeof(TOF), typeof(TP), typeof(AND), typeof(OR), typeof(NOT), typeof(XOR),
|
|
398
|
+
typeof(NAND), typeof(NOR), typeof(SR), typeof(RS), typeof(R_TRIG), typeof(F_TRIG),
|
|
399
|
+
typeof(CTU), typeof(CTD), typeof(CTUD), typeof(EQ), typeof(NE), typeof(LT),
|
|
400
|
+
typeof(GT), typeof(GE), typeof(LE), typeof(MOVE), typeof(SEL), typeof(MUX),
|
|
401
|
+
typeof(MIN), typeof(MAX), typeof(LIMIT), typeof(ASSIGNMENT)
|
|
402
|
+
};
|
|
403
|
+
foreach (var t in types)
|
|
404
|
+
JsEngine.SetValue(t.Name, TypeReference.CreateTypeReference(JsEngine, t));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
public class RefVar<T> where T : struct
|
|
410
|
+
{
|
|
411
|
+
public static implicit operator T(RefVar<T> r)
|
|
412
|
+
{
|
|
413
|
+
return r.Value;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
private readonly NodalisEngine _engine;
|
|
417
|
+
private readonly string _address;
|
|
418
|
+
|
|
419
|
+
public RefVar(NodalisEngine engine, string address)
|
|
420
|
+
{
|
|
421
|
+
_engine = engine;
|
|
422
|
+
_address = address;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
public T Value
|
|
426
|
+
{
|
|
427
|
+
get => Read();
|
|
428
|
+
set => Write(value);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
public string Address => _address;
|
|
432
|
+
|
|
433
|
+
private T Read()
|
|
434
|
+
{
|
|
435
|
+
if (typeof(T) == typeof(bool))
|
|
436
|
+
return (T)(object)_engine.ReadBit(_address);
|
|
437
|
+
if (typeof(T) == typeof(byte))
|
|
438
|
+
return (T)(object)_engine.ReadByte(_address);
|
|
439
|
+
if (typeof(T) == typeof(ushort))
|
|
440
|
+
return (T)(object)_engine.ReadWord(_address);
|
|
441
|
+
if (typeof(T) == typeof(uint))
|
|
442
|
+
return (T)(object)_engine.ReadDWord(_address);
|
|
443
|
+
throw new NotSupportedException($"Unsupported RefVar type: {typeof(T)}");
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private void Write(T value)
|
|
447
|
+
{
|
|
448
|
+
if (typeof(T) == typeof(bool))
|
|
449
|
+
_engine.WriteBit(_address, (bool)(object)value);
|
|
450
|
+
else if (typeof(T) == typeof(byte))
|
|
451
|
+
_engine.WriteByte(_address, (byte)(object)value);
|
|
452
|
+
else if (typeof(T) == typeof(ushort))
|
|
453
|
+
_engine.WriteWord(_address, (ushort)(object)value);
|
|
454
|
+
else if (typeof(T) == typeof(uint))
|
|
455
|
+
_engine.WriteDWord(_address, (uint)(object)value);
|
|
456
|
+
else throw new NotSupportedException($"Unsupported RefVar type: {typeof(T)}");
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
public class TON
|
|
461
|
+
{
|
|
462
|
+
public bool IN;
|
|
463
|
+
public long PT;
|
|
464
|
+
public bool Q;
|
|
465
|
+
public long ET;
|
|
466
|
+
private long start = 0;
|
|
467
|
+
|
|
468
|
+
public void call()
|
|
469
|
+
{
|
|
470
|
+
if (IN)
|
|
471
|
+
{
|
|
472
|
+
if (start == 0) start = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
473
|
+
ET = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - start;
|
|
474
|
+
Q = ET >= PT;
|
|
475
|
+
}
|
|
476
|
+
else
|
|
477
|
+
{
|
|
478
|
+
start = 0;
|
|
479
|
+
ET = 0;
|
|
480
|
+
Q = false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
public class TOF
|
|
486
|
+
{
|
|
487
|
+
public bool IN;
|
|
488
|
+
public long PT;
|
|
489
|
+
public bool Q;
|
|
490
|
+
public long ET;
|
|
491
|
+
private long start = 0;
|
|
492
|
+
|
|
493
|
+
public void call()
|
|
494
|
+
{
|
|
495
|
+
if (IN)
|
|
496
|
+
{
|
|
497
|
+
Q = true;
|
|
498
|
+
start = 0;
|
|
499
|
+
ET = 0;
|
|
500
|
+
}
|
|
501
|
+
else if (Q)
|
|
502
|
+
{
|
|
503
|
+
if (start == 0) start = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
504
|
+
ET = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - start;
|
|
505
|
+
if (ET >= PT) Q = false;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
public class TP
|
|
511
|
+
{
|
|
512
|
+
public bool IN;
|
|
513
|
+
public long PT;
|
|
514
|
+
public bool Q;
|
|
515
|
+
public long ET;
|
|
516
|
+
private bool lastIN = false;
|
|
517
|
+
private long start = 0;
|
|
518
|
+
|
|
519
|
+
public void call()
|
|
520
|
+
{
|
|
521
|
+
Q = false;
|
|
522
|
+
if (!lastIN && IN)
|
|
523
|
+
{
|
|
524
|
+
lastIN = IN;
|
|
525
|
+
ET = 0;
|
|
526
|
+
start = 0;
|
|
527
|
+
}
|
|
528
|
+
if (IN)
|
|
529
|
+
{
|
|
530
|
+
Q = true;
|
|
531
|
+
}
|
|
532
|
+
else if (lastIN && !IN)
|
|
533
|
+
{
|
|
534
|
+
if (start == 0) start = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
535
|
+
ET = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - start;
|
|
536
|
+
if (PT >= ET) Q = true;
|
|
537
|
+
else lastIN = false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
public class AND
|
|
543
|
+
{
|
|
544
|
+
public bool IN1;
|
|
545
|
+
public bool IN2;
|
|
546
|
+
public bool OUT;
|
|
547
|
+
public void call() => OUT = IN1 && IN2;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
public class OR
|
|
551
|
+
{
|
|
552
|
+
public bool IN1;
|
|
553
|
+
public bool IN2;
|
|
554
|
+
public bool OUT;
|
|
555
|
+
public void call() => OUT = IN1 || IN2;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
public class NOT
|
|
559
|
+
{
|
|
560
|
+
public bool IN;
|
|
561
|
+
public bool OUT;
|
|
562
|
+
public void call() => OUT = !IN;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
public class XOR
|
|
566
|
+
{
|
|
567
|
+
public bool IN1;
|
|
568
|
+
public bool IN2;
|
|
569
|
+
public bool OUT;
|
|
570
|
+
public void call() => OUT = IN1 ^ IN2;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
public class NAND
|
|
574
|
+
{
|
|
575
|
+
public bool IN1;
|
|
576
|
+
public bool IN2;
|
|
577
|
+
public bool OUT;
|
|
578
|
+
public void call() => OUT = !(IN1 && IN2);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
public class NOR
|
|
582
|
+
{
|
|
583
|
+
public bool IN1;
|
|
584
|
+
public bool IN2;
|
|
585
|
+
public bool OUT;
|
|
586
|
+
public void call() => OUT = !(IN1 || IN2);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
public class SR
|
|
590
|
+
{
|
|
591
|
+
public bool S1;
|
|
592
|
+
public bool R;
|
|
593
|
+
public bool Q1;
|
|
594
|
+
public void call()
|
|
595
|
+
{
|
|
596
|
+
if (R) Q1 = false;
|
|
597
|
+
if (S1) Q1 = true;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
public class RS
|
|
602
|
+
{
|
|
603
|
+
public bool S;
|
|
604
|
+
public bool R1;
|
|
605
|
+
public bool Q1;
|
|
606
|
+
public void call()
|
|
607
|
+
{
|
|
608
|
+
if (S) Q1 = true;
|
|
609
|
+
if (R1) Q1 = false;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
public class R_TRIG
|
|
614
|
+
{
|
|
615
|
+
public bool CLK;
|
|
616
|
+
public bool OUT;
|
|
617
|
+
private bool last;
|
|
618
|
+
public void call()
|
|
619
|
+
{
|
|
620
|
+
OUT = CLK && !last;
|
|
621
|
+
last = CLK;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
public class F_TRIG
|
|
626
|
+
{
|
|
627
|
+
public bool CLK;
|
|
628
|
+
public bool OUT;
|
|
629
|
+
private bool last;
|
|
630
|
+
public void call()
|
|
631
|
+
{
|
|
632
|
+
OUT = !CLK && last;
|
|
633
|
+
last = CLK;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
public class CTU
|
|
638
|
+
{
|
|
639
|
+
public bool CU;
|
|
640
|
+
public bool R;
|
|
641
|
+
public ushort PV;
|
|
642
|
+
public ushort CV;
|
|
643
|
+
public bool Q;
|
|
644
|
+
private bool lastCU;
|
|
645
|
+
|
|
646
|
+
public void call()
|
|
647
|
+
{
|
|
648
|
+
if (R)
|
|
649
|
+
CV = 0;
|
|
650
|
+
else if (CU && !lastCU)
|
|
651
|
+
CV++;
|
|
652
|
+
Q = CV >= PV;
|
|
653
|
+
lastCU = CU;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
public class CTD
|
|
658
|
+
{
|
|
659
|
+
public bool CD;
|
|
660
|
+
public bool LD;
|
|
661
|
+
public ushort PV;
|
|
662
|
+
public ushort CV;
|
|
663
|
+
public bool Q;
|
|
664
|
+
private bool lastCD;
|
|
665
|
+
|
|
666
|
+
public void call()
|
|
667
|
+
{
|
|
668
|
+
if (LD)
|
|
669
|
+
CV = PV;
|
|
670
|
+
else if (CD && !lastCD && CV > 0)
|
|
671
|
+
CV--;
|
|
672
|
+
Q = CV == 0;
|
|
673
|
+
lastCD = CD;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
public class CTUD
|
|
678
|
+
{
|
|
679
|
+
public bool CU;
|
|
680
|
+
public bool CD;
|
|
681
|
+
public bool R;
|
|
682
|
+
public bool LD;
|
|
683
|
+
public ushort PV;
|
|
684
|
+
public ushort CV;
|
|
685
|
+
public bool QU;
|
|
686
|
+
public bool QD;
|
|
687
|
+
private bool lastCU;
|
|
688
|
+
private bool lastCD;
|
|
689
|
+
|
|
690
|
+
public void call()
|
|
691
|
+
{
|
|
692
|
+
if (R)
|
|
693
|
+
CV = 0;
|
|
694
|
+
else if (LD)
|
|
695
|
+
CV = PV;
|
|
696
|
+
else
|
|
697
|
+
{
|
|
698
|
+
if (CU && !lastCU) CV++;
|
|
699
|
+
if (CD && !lastCD && CV > 0) CV--;
|
|
700
|
+
}
|
|
701
|
+
QU = CV >= PV;
|
|
702
|
+
QD = CV == 0;
|
|
703
|
+
lastCU = CU;
|
|
704
|
+
lastCD = CD;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
public class EQ
|
|
709
|
+
{
|
|
710
|
+
public uint IN1;
|
|
711
|
+
public uint IN2;
|
|
712
|
+
public bool OUT;
|
|
713
|
+
public void call() => OUT = IN1 == IN2;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
public class NE
|
|
717
|
+
{
|
|
718
|
+
public uint IN1;
|
|
719
|
+
public uint IN2;
|
|
720
|
+
public bool OUT;
|
|
721
|
+
public void call() => OUT = IN1 != IN2;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
public class LT
|
|
725
|
+
{
|
|
726
|
+
public uint IN1;
|
|
727
|
+
public uint IN2;
|
|
728
|
+
public bool OUT;
|
|
729
|
+
public void call() => OUT = IN1 < IN2;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
public class GT
|
|
733
|
+
{
|
|
734
|
+
public uint IN1;
|
|
735
|
+
public uint IN2;
|
|
736
|
+
public bool OUT;
|
|
737
|
+
public void call() => OUT = IN1 > IN2;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
public class GE
|
|
741
|
+
{
|
|
742
|
+
public uint IN1;
|
|
743
|
+
public uint IN2;
|
|
744
|
+
public bool OUT;
|
|
745
|
+
public void call() => OUT = IN1 >= IN2;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
public class LE
|
|
749
|
+
{
|
|
750
|
+
public uint IN1;
|
|
751
|
+
public uint IN2;
|
|
752
|
+
public bool OUT;
|
|
753
|
+
public void call() => OUT = IN1 <= IN2;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
public class MOVE
|
|
757
|
+
{
|
|
758
|
+
public uint IN;
|
|
759
|
+
public uint OUT;
|
|
760
|
+
public void call() => OUT = IN;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
public class SEL
|
|
764
|
+
{
|
|
765
|
+
public bool G;
|
|
766
|
+
public uint IN0;
|
|
767
|
+
public uint IN1;
|
|
768
|
+
public uint OUT;
|
|
769
|
+
public void call() => OUT = G ? IN1 : IN0;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
public class MUX
|
|
773
|
+
{
|
|
774
|
+
public bool K;
|
|
775
|
+
public uint IN0;
|
|
776
|
+
public uint IN1;
|
|
777
|
+
public uint OUT;
|
|
778
|
+
public void call() => OUT = K ? IN1 : IN0;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
public class MIN
|
|
782
|
+
{
|
|
783
|
+
public uint IN1;
|
|
784
|
+
public uint IN2;
|
|
785
|
+
public uint OUT;
|
|
786
|
+
public void call() => OUT = Math.Min(IN1, IN2);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
public class MAX
|
|
790
|
+
{
|
|
791
|
+
public uint IN1;
|
|
792
|
+
public uint IN2;
|
|
793
|
+
public uint OUT;
|
|
794
|
+
public void call() => OUT = Math.Max(IN1, IN2);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
public class LIMIT
|
|
798
|
+
{
|
|
799
|
+
public uint MN;
|
|
800
|
+
public uint IN;
|
|
801
|
+
public uint MX;
|
|
802
|
+
public uint OUT;
|
|
803
|
+
public void call()
|
|
804
|
+
{
|
|
805
|
+
if (IN < MN) OUT = MN;
|
|
806
|
+
else if (IN > MX) OUT = MX;
|
|
807
|
+
else OUT = IN;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
public class ASSIGNMENT
|
|
812
|
+
{
|
|
813
|
+
public bool IN;
|
|
814
|
+
public bool OUT;
|
|
815
|
+
public void call() => OUT = IN;
|
|
816
|
+
}
|
|
817
|
+
}
|