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.
Files changed (37) hide show
  1. package/README.md +134 -0
  2. package/package.json +59 -0
  3. package/src/compilers/CPPCompiler.js +272 -0
  4. package/src/compilers/Compiler.js +108 -0
  5. package/src/compilers/JSCompiler.js +293 -0
  6. package/src/compilers/iec-parser/parser.js +4254 -0
  7. package/src/compilers/st-parser/expressionConverter.js +155 -0
  8. package/src/compilers/st-parser/gcctranspiler.js +237 -0
  9. package/src/compilers/st-parser/jstranspiler.js +254 -0
  10. package/src/compilers/st-parser/parser.js +367 -0
  11. package/src/compilers/st-parser/tokenizer.js +78 -0
  12. package/src/compilers/support/generic/json.hpp +25526 -0
  13. package/src/compilers/support/generic/modbus.cpp +378 -0
  14. package/src/compilers/support/generic/modbus.h +124 -0
  15. package/src/compilers/support/generic/nodalis.cpp +421 -0
  16. package/src/compilers/support/generic/nodalis.h +798 -0
  17. package/src/compilers/support/generic/opcua.cpp +267 -0
  18. package/src/compilers/support/generic/opcua.h +50 -0
  19. package/src/compilers/support/generic/open62541.c +151897 -0
  20. package/src/compilers/support/generic/open62541.h +50357 -0
  21. package/src/compilers/support/jint/nodalis/Nodalis.sln +28 -0
  22. package/src/compilers/support/jint/nodalis/NodalisEngine/ModbusClient.cs +200 -0
  23. package/src/compilers/support/jint/nodalis/NodalisEngine/NodalisEngine.cs +817 -0
  24. package/src/compilers/support/jint/nodalis/NodalisEngine/NodalisEngine.csproj +16 -0
  25. package/src/compilers/support/jint/nodalis/NodalisEngine/OPCClient.cs +172 -0
  26. package/src/compilers/support/jint/nodalis/NodalisEngine/OPCServer.cs +275 -0
  27. package/src/compilers/support/jint/nodalis/NodalisPLC/NodalisPLC.csproj +19 -0
  28. package/src/compilers/support/jint/nodalis/NodalisPLC/Program.cs +197 -0
  29. package/src/compilers/support/jint/nodalis/NodalisPLC/bootstrap.bat +5 -0
  30. package/src/compilers/support/jint/nodalis/NodalisPLC/bootstrap.sh +5 -0
  31. package/src/compilers/support/jint/nodalis/build.bat +25 -0
  32. package/src/compilers/support/jint/nodalis/build.sh +31 -0
  33. package/src/compilers/support/nodejs/IOClient.js +110 -0
  34. package/src/compilers/support/nodejs/modbus.js +115 -0
  35. package/src/compilers/support/nodejs/nodalis.js +662 -0
  36. package/src/compilers/support/nodejs/opcua.js +194 -0
  37. package/src/nodalis.js +174 -0
@@ -0,0 +1,16 @@
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <TargetFramework>netstandard2.1</TargetFramework>
5
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
6
+ </PropertyGroup>
7
+ <PropertyGroup>
8
+ <LangVersion>latest</LangVersion>
9
+ </PropertyGroup>
10
+ <ItemGroup>
11
+ <PackageReference Include="Jint" Version="3.0.0-beta-2031" />
12
+ <PackageReference Include="System.Text.Json" Version="8.0.3" />
13
+ <PackageReference Include="OPCFoundation.NetStandard.Opc.Ua" Version="1.4.368.49" />
14
+ </ItemGroup>
15
+
16
+ </Project>
@@ -0,0 +1,172 @@
1
+ // Copyright [2025] Nathan Skipper
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ /// <summary>
16
+ /// OPC client implementation for .NET
17
+ /// </summary>
18
+
19
+ using Opc.Ua;
20
+ using Opc.Ua.Client;
21
+ using Opc.Ua.Configuration;
22
+ using System;
23
+ using System.Collections.Generic;
24
+ using System.Threading.Tasks;
25
+
26
+ namespace Nodalis
27
+ {
28
+ public class OPCClient : IOClient
29
+ {
30
+ private Session? _session;
31
+ private ApplicationConfiguration? _config;
32
+
33
+ public OPCClient() : base("OPCUA") { }
34
+
35
+ public override void Connect()
36
+ {
37
+ Task.Run(async () =>
38
+ {
39
+ try
40
+ {
41
+ var map = mappings[0];
42
+ string endpointUrl = map.moduleID;
43
+
44
+ _config = new ApplicationConfiguration
45
+ {
46
+ ApplicationName = "NodalisOPCUAClient",
47
+ ApplicationType = ApplicationType.Client,
48
+ SecurityConfiguration = new SecurityConfiguration
49
+ {
50
+ ApplicationCertificate = new CertificateIdentifier(),
51
+ AutoAcceptUntrustedCertificates = true,
52
+ },
53
+ TransportConfigurations = new TransportConfigurationCollection(),
54
+ TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
55
+ ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
56
+ };
57
+ await _config.Validate(ApplicationType.Client);
58
+
59
+ var app = new ApplicationInstance { ApplicationName = "NodalisOPCUAClient", ApplicationType = ApplicationType.Client, ApplicationConfiguration = _config };
60
+ var endpoint = CoreClientUtils.SelectEndpoint(endpointUrl, false, 15000);
61
+ var endpointConfig = EndpointConfiguration.Create(_config);
62
+ var endpointDesc = new ConfiguredEndpoint(null, endpoint, endpointConfig);
63
+
64
+ _session = await Session.Create(_config, endpointDesc, false, "", 60000, null, null);
65
+ connected = true;
66
+ Console.WriteLine("OPC UA connected.");
67
+ }
68
+ catch (Exception ex)
69
+ {
70
+ Console.WriteLine("OPC UA connection error: " + ex.Message);
71
+ connected = false;
72
+ }
73
+ });
74
+ }
75
+
76
+ private NodeId GetNodeId(string remote) => new NodeId($"s={remote}", 1);
77
+
78
+ public override bool ReadBit(string address, out int result)
79
+ {
80
+ return ReadValue(address, out result, BuiltInType.Boolean);
81
+ }
82
+
83
+ public override bool WriteBit(string address, int value)
84
+ {
85
+ return WriteVal(address, value != 0, BuiltInType.Boolean);
86
+ }
87
+
88
+ public override bool ReadByte(string address, out byte result)
89
+ {
90
+ return ReadValue(address, out result, BuiltInType.Byte);
91
+ }
92
+
93
+ public override bool WriteByte(string address, byte value)
94
+ {
95
+ return WriteVal(address, value, BuiltInType.Byte);
96
+ }
97
+
98
+ public override bool ReadWord(string address, out ushort result)
99
+ {
100
+ return ReadValue(address, out result, BuiltInType.UInt16);
101
+ }
102
+
103
+ public override bool WriteWord(string address, ushort value)
104
+ {
105
+ return WriteVal(address, value, BuiltInType.UInt16);
106
+ }
107
+
108
+ public override bool ReadDWord(string address, out uint result)
109
+ {
110
+ return ReadValue(address, out result, BuiltInType.UInt32);
111
+ }
112
+
113
+ public override bool WriteDWord(string address, uint value)
114
+ {
115
+ return WriteVal(address, value, BuiltInType.UInt32);
116
+ }
117
+
118
+ private bool ReadValue<T>(string address, out T result, BuiltInType type)
119
+ {
120
+ result = default!;
121
+ if (!connected || _session == null) return false;
122
+
123
+ try
124
+ {
125
+ var nodeId = GetNodeId(address);
126
+ var value = _session.ReadValue(nodeId);
127
+ if (value.Value is T cast)
128
+ {
129
+ result = cast;
130
+ return true;
131
+ }
132
+ }
133
+ catch (Exception ex)
134
+ {
135
+ Console.WriteLine($"Read error [{address}]: {ex.Message}");
136
+ }
137
+ return false;
138
+ }
139
+
140
+ private bool WriteVal<T>(string address, T value, BuiltInType type)
141
+ {
142
+ if (!connected || this._session == null) return false;
143
+
144
+ try
145
+ {
146
+ var writeValue = new WriteValue
147
+ {
148
+ NodeId = GetNodeId(address),
149
+ AttributeId = Attributes.Value,
150
+ Value = new DataValue(new Variant(value))
151
+ };
152
+
153
+ writeValue.Value.StatusCode = StatusCodes.Good;
154
+ writeValue.Value.ServerTimestamp = DateTime.MinValue;
155
+ writeValue.Value.SourceTimestamp = DateTime.MinValue;
156
+
157
+ var writeResults = _session.Write(null, new WriteValueCollection { writeValue }, out StatusCodeCollection statusCodes, out DiagnosticInfoCollection diag);
158
+ if (StatusCode.IsBad(statusCodes[0]))
159
+ {
160
+ Console.WriteLine($"Write failed for {address}: {statusCodes[0]}");
161
+ return false;
162
+ }
163
+ return true;
164
+ }
165
+ catch (Exception ex)
166
+ {
167
+ Console.WriteLine($"Write error [{address}]: {ex.Message}");
168
+ }
169
+ return false;
170
+ }
171
+ }
172
+ }
@@ -0,0 +1,275 @@
1
+ // Copyright [2025] Nathan Skipper
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ /// <summary>
16
+ /// OPC Server implementation for .NET
17
+ /// </summary>
18
+ ///
19
+ using Opc.Ua;
20
+ using Opc.Ua.Configuration;
21
+ using Opc.Ua.Server;
22
+ using System;
23
+ using System.Collections.Generic;
24
+ using System.Threading.Tasks;
25
+
26
+ namespace Nodalis
27
+ {
28
+ public class OPCServer
29
+ {
30
+ private ApplicationInstance _application;
31
+ private NodalisEngine _engine;
32
+ private Dictionary<string, string> _addressMap = new();
33
+ private NodalisNodeManager? _nodeManager;
34
+ private StandardServer? _server;
35
+
36
+ public OPCServer(NodalisEngine engine)
37
+ {
38
+ _engine = engine;
39
+ }
40
+
41
+ public void MapVariable(string varName, string address)
42
+ {
43
+ _addressMap[varName] = address;
44
+ }
45
+
46
+ public async Task StartAsync()
47
+ {
48
+ _application = new ApplicationInstance
49
+ {
50
+ ApplicationName = "NodalisServer",
51
+ ApplicationType = ApplicationType.Server,
52
+ ConfigSectionName = "NodalisServer"
53
+ };
54
+
55
+ var config = new ApplicationConfiguration
56
+ {
57
+ ApplicationName = "NodalisServer",
58
+ ApplicationType = ApplicationType.Server,
59
+ ApplicationUri = "urn:localhost:NodalisServer",
60
+ ServerConfiguration = new ServerConfiguration
61
+ {
62
+ BaseAddresses = { "opc.tcp://localhost:4840/UA/Nodalis" }
63
+ },
64
+ TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
65
+ SecurityConfiguration = new SecurityConfiguration
66
+ {
67
+ ApplicationCertificate = new CertificateIdentifier(),
68
+ AutoAcceptUntrustedCertificates = true
69
+ },
70
+ CertificateValidator = new CertificateValidator(),
71
+ //DiagnosticsConfiguration = new DiagnosticsConfiguration { Enabled = true },
72
+ Extensions = new XmlElementCollection()
73
+ };
74
+
75
+ await config.Validate(ApplicationType.Server);
76
+
77
+ _application.ApplicationConfiguration = config;
78
+
79
+ _server = new NodalisServer(_engine, _addressMap);
80
+ _application.Start(_server);
81
+
82
+ Console.WriteLine("OPC UA Server started at: opc.tcp://localhost:4840/UA/Nodalis");
83
+ }
84
+
85
+ public async Task StopAsync()
86
+ {
87
+ if (_server != null)
88
+ {
89
+ _server.Stop();
90
+ Console.WriteLine("OPC UA Server stopped.");
91
+ }
92
+ }
93
+
94
+ private class NodalisServer : StandardServer
95
+ {
96
+ private readonly NodalisEngine _engine;
97
+ private readonly Dictionary<string, string> _map;
98
+
99
+ public NodalisServer(NodalisEngine engine, Dictionary<string, string> map)
100
+ {
101
+ _engine = engine;
102
+ _map = map;
103
+ }
104
+
105
+ protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration config)
106
+ {
107
+ var nodeManagers = new List<INodeManager>
108
+ {
109
+ new NodalisNodeManager(server, config, _engine, _map)
110
+ };
111
+
112
+ return new MasterNodeManager(server, config, null, nodeManagers.ToArray());
113
+ }
114
+ }
115
+
116
+ private class NodalisNodeManager : CustomNodeManager2
117
+ {
118
+ private readonly NodalisEngine _engine;
119
+ private readonly Dictionary<string, string> _addressMap;
120
+
121
+ public NodalisNodeManager(IServerInternal server, ApplicationConfiguration config, NodalisEngine engine, Dictionary<string, string> map)
122
+ : base(server, config, "http://nodalis.local/UA/")
123
+ {
124
+ _engine = engine;
125
+ _addressMap = map;
126
+ SystemContext.NodeIdFactory = this;
127
+ }
128
+
129
+ private FolderState CreateFolder(
130
+ NodeState parent,
131
+ string path,
132
+ string name,
133
+ IDictionary<NodeId, IList<IReference>> externalReferences,
134
+ ushort namespaceIndex)
135
+ {
136
+ var folder = new FolderState(parent)
137
+ {
138
+ SymbolicName = name,
139
+ ReferenceTypeId = ReferenceTypeIds.Organizes,
140
+ TypeDefinitionId = ObjectTypeIds.FolderType,
141
+ NodeId = new NodeId(path, namespaceIndex),
142
+ BrowseName = new QualifiedName(name, namespaceIndex),
143
+ DisplayName = name,
144
+ EventNotifier = EventNotifiers.None
145
+ };
146
+
147
+ if (externalReferences.TryGetValue(ObjectIds.ObjectsFolder, out var references))
148
+ references.Add(new NodeStateReference(ReferenceTypeIds.Organizes, false, folder.NodeId));
149
+ else
150
+ externalReferences[ObjectIds.ObjectsFolder] = new List<IReference>
151
+ {
152
+ new NodeStateReference(ReferenceTypeIds.Organizes, false, folder.NodeId)
153
+ };
154
+
155
+ AddPredefinedNode(SystemContext, folder);
156
+ return folder;
157
+ }
158
+
159
+ private ServiceResult ReadValueHandler(
160
+ ISystemContext context,
161
+ NodeState node,
162
+ NumericRange indexRange,
163
+ QualifiedName name,
164
+ ref object value,
165
+ ref StatusCode statusCode,
166
+ ref DateTime timestamp)
167
+ {
168
+ if (node is BaseDataVariableState variable &&
169
+ _addressMap.TryGetValue(variable.SymbolicName, out var addr))
170
+ {
171
+ try
172
+ {
173
+ value = ReadFromEngine(addr);
174
+ statusCode = StatusCodes.Good;
175
+ timestamp = DateTime.UtcNow;
176
+ return ServiceResult.Good;
177
+ }
178
+ catch (Exception ex)
179
+ {
180
+ Console.WriteLine($"Read error for {addr}: {ex.Message}");
181
+ return StatusCodes.BadUnexpectedError;
182
+ }
183
+ }
184
+
185
+ return StatusCodes.BadNodeIdUnknown;
186
+ }
187
+
188
+ public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)
189
+ {
190
+ var folder = CreateFolder(
191
+ null,
192
+ "Nodalis",
193
+ "Nodalis",
194
+ externalReferences,
195
+ NamespaceIndex
196
+ );
197
+
198
+ foreach (var entry in _addressMap)
199
+ {
200
+ string name = entry.Key;
201
+ string addr = entry.Value;
202
+ var dataType = GetDataType(addr);
203
+
204
+ var variable = new BaseDataVariableState(folder)
205
+ {
206
+ SymbolicName = name,
207
+ NodeId = new NodeId(name, NamespaceIndex),
208
+ BrowseName = new QualifiedName(name, NamespaceIndex),
209
+ DisplayName = name,
210
+ DataType = dataType,
211
+ TypeDefinitionId = VariableTypeIds.BaseDataVariableType,
212
+ ValueRank = ValueRanks.Scalar,
213
+ AccessLevel = AccessLevels.CurrentReadOrWrite,
214
+ UserAccessLevel = AccessLevels.CurrentReadOrWrite
215
+ };
216
+
217
+ // Define read delegate
218
+ variable.OnReadValue = ReadValueHandler;
219
+
220
+
221
+ // Define write delegate
222
+ variable.OnSimpleWriteValue = (ISystemContext context, NodeState node, ref object val) =>
223
+ {
224
+ try
225
+ {
226
+ WriteToEngine(addr, val);
227
+ return ServiceResult.Good;
228
+ }
229
+ catch (Exception ex)
230
+ {
231
+ Console.WriteLine($"Write error for {addr}: {ex.Message}");
232
+ return StatusCodes.BadUnexpectedError;
233
+ }
234
+ };
235
+
236
+ AddPredefinedNode(SystemContext, variable);
237
+ }
238
+ }
239
+
240
+ private object ReadFromEngine(string address)
241
+ {
242
+ if (address.Contains(".")) return _engine.ReadBit(address);
243
+ else if (address.Contains("X")) return _engine.ReadByte(address);
244
+ else if (address.Contains("W")) return _engine.ReadWord(address);
245
+ else if (address.Contains("D")) return _engine.ReadDWord(address);
246
+ return false;
247
+ }
248
+
249
+ private void WriteToEngine(string address, object value)
250
+ {
251
+ switch (value)
252
+ {
253
+ case bool b: _engine.WriteBit(address, b); break;
254
+ case byte bt: _engine.WriteByte(address, bt); break;
255
+ case ushort us: _engine.WriteWord(address, us); break;
256
+ case uint ui: _engine.WriteDWord(address, ui); break;
257
+ default: throw new InvalidCastException($"Unsupported value type: {value?.GetType()?.Name}");
258
+ }
259
+ }
260
+
261
+
262
+ private NodeId GetDataType(string address)
263
+ {
264
+ if (address.Contains(".")) return DataTypeIds.Boolean;
265
+ else if (address.Contains("X")) return DataTypeIds.Byte;
266
+ else if (address.Contains("W")) return DataTypeIds.UInt16;
267
+ else if (address.Contains("D")) return DataTypeIds.UInt32;
268
+ return DataTypeIds.Boolean;
269
+ }
270
+
271
+
272
+
273
+ }
274
+ }
275
+ }
@@ -0,0 +1,19 @@
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <OutputType>Exe</OutputType>
5
+ <TargetFramework>net8.0</TargetFramework>
6
+ <RuntimeIdentifiers>win-x64;linux-x64;osx-x64;osx-arm64</RuntimeIdentifiers>
7
+ <ImplicitUsings>enable</ImplicitUsings>
8
+ <Nullable>enable</Nullable>
9
+ </PropertyGroup>
10
+
11
+ <ItemGroup>
12
+ <PackageReference Include="Jint" Version="3.0.0-beta-2031" />
13
+ <PackageReference Include="System.Text.Json" Version="8.0.3" />
14
+ </ItemGroup>
15
+ <ItemGroup>
16
+ <ProjectReference Include="..\\NodalisEngine\\NodalisEngine.csproj" />
17
+ </ItemGroup>
18
+
19
+ </Project>
@@ -0,0 +1,197 @@
1
+ // Copyright [2025] Nathan Skipper
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ /// <summary>
16
+ /// Nodalis PLC .NET Console app
17
+ /// </summary>
18
+
19
+ using System;
20
+ using System.IO;
21
+ using System.Threading;
22
+ using Jint;
23
+ using Jint.Runtime;
24
+ using Nodalis;
25
+ using System.Text.RegularExpressions;
26
+
27
+
28
+ class ProgramEngine : NodalisEngine
29
+ {
30
+ private static readonly ulong[,] MEMORY = new ulong[64, 16];
31
+
32
+ public override bool ReadBit(string address)
33
+ {
34
+ var mem = ParseAddress(address);
35
+ return (GetMemoryCell(mem[0], mem[2]) & (1UL << (mem[3] % 64))) != 0;
36
+ }
37
+
38
+ public override byte ReadByte(string address)
39
+ {
40
+ var mem = ParseAddress(address);
41
+ return (byte)((GetMemoryCell(mem[0], mem[2]) >> (mem[3] % 64)) & 0xFF);
42
+ }
43
+
44
+ public override ushort ReadWord(string address)
45
+ {
46
+ var mem = ParseAddress(address);
47
+ return (ushort)((GetMemoryCell(mem[0], mem[2]) >> (mem[3] % 64)) & 0xFFFF);
48
+ }
49
+
50
+ public override uint ReadDWord(string address)
51
+ {
52
+ var mem = ParseAddress(address);
53
+ return (uint)((GetMemoryCell(mem[0], mem[2]) >> (mem[3] % 64)) & 0xFFFFFFFF);
54
+ }
55
+
56
+ public override void WriteBit(string address, bool value)
57
+ {
58
+ var mem = ParseAddress(address);
59
+ if (value)
60
+ GetMemoryCell(mem[0], mem[2]) |= (1UL << (mem[3] % 64));
61
+ else
62
+ GetMemoryCell(mem[0], mem[2]) &= ~(1UL << (mem[3] % 64));
63
+ }
64
+
65
+ public override void WriteByte(string address, byte value)
66
+ {
67
+ WriteGeneric(address, value, 8);
68
+ }
69
+
70
+ public override void WriteWord(string address, ushort value)
71
+ {
72
+ WriteGeneric(address, value, 16);
73
+ }
74
+
75
+ public override void WriteDWord(string address, uint value)
76
+ {
77
+ WriteGeneric(address, value, 32);
78
+ }
79
+
80
+ public enum MemorySpace
81
+ {
82
+ I = 0,
83
+ Q = 1,
84
+ M = 2
85
+ }
86
+
87
+ public static ref ulong GetMemoryCell(int space, int addr)
88
+ {
89
+ int r = -1, c = 0, b = 0;
90
+ switch (space)
91
+ {
92
+ case 1: // Q
93
+ r = (addr * 8) / 64;
94
+ c = 1;
95
+ b = addr % 8;
96
+ break;
97
+ case 0: // I
98
+ r = (addr * 8) / 64;
99
+ c = 0;
100
+ b = addr % 8;
101
+ break;
102
+ case 2: // M
103
+ r = (addr * 8) / (64 * 14);
104
+ c = (addr / 112) + 2;
105
+ b = addr % 8;
106
+ break;
107
+ }
108
+
109
+ if (r >= 0 && r < 64 && c >= 0 && c < 16)
110
+ {
111
+ return ref MEMORY[r, c];
112
+ }
113
+ throw new ArgumentOutOfRangeException();
114
+ }
115
+
116
+ public static List<int> ParseAddress(string address)
117
+ {
118
+ var pattern = new Regex(@"%([IQM])([XBWDL])(\d+)(?:\.(\d+))?", RegexOptions.IgnoreCase);
119
+ var match = pattern.Match(address);
120
+
121
+ if (!match.Success)
122
+ throw new ArgumentException($"Invalid address format: {address}");
123
+
124
+ string space = match.Groups[1].Value.ToUpperInvariant(); // I, Q, M
125
+ string type = match.Groups[2].Value.ToUpperInvariant(); // X, W, D, etc.
126
+ string index = match.Groups[3].Value; // 0, 1, ...
127
+ string bit = match.Groups[4].Success ? match.Groups[4].Value : null;
128
+
129
+ int ispace = space switch
130
+ {
131
+ "M" => (int)MemorySpace.M,
132
+ "Q" => (int)MemorySpace.Q,
133
+ "I" => (int)MemorySpace.I,
134
+ _ => throw new ArgumentException($"Unknown memory space: {space}")
135
+ };
136
+
137
+ int width = type switch
138
+ {
139
+ "X" => 8,
140
+ "W" => 16,
141
+ "D" => 32,
142
+ _ => throw new ArgumentException($"Unknown type: {type}")
143
+ };
144
+
145
+ int addr = int.Parse(index);
146
+ int ibit = bit != null ? int.Parse(bit) : -1;
147
+
148
+ return new List<int> { ispace, width, addr, ibit };
149
+ }
150
+
151
+ private void WriteGeneric(string address, ulong value, int width)
152
+ {
153
+ var mem = ParseAddress(address);
154
+ ulong mask = (1UL << width) - 1;
155
+ ulong shiftedMask = mask << (mem[3] % 64);
156
+ ulong location = GetMemoryCell(mem[0], mem[2]);
157
+ location &= ~shiftedMask;
158
+ location |= (value & mask) << (mem[3] % 64);
159
+ }
160
+ }
161
+
162
+ class Program
163
+ {
164
+ static void Main(string[] args)
165
+ {
166
+ if (args.Length < 1)
167
+ {
168
+ Console.WriteLine("Usage: NodalisRuntime <jsfile>");
169
+ return;
170
+ }
171
+
172
+ var engine = new ProgramEngine();
173
+ long lastExec = engine.ElapsedMilliseconds;
174
+ string jsCode = File.ReadAllText(args[0]);
175
+ try
176
+ {
177
+ engine.Load(jsCode);
178
+ engine.Setup();
179
+ while (true)
180
+ {
181
+ engine.SuperviseIO();
182
+ if (engine.ElapsedMilliseconds - lastExec >= 100)
183
+ {
184
+ lastExec = engine.ElapsedMilliseconds;
185
+ engine.Execute();
186
+ }
187
+ Thread.Sleep(1);
188
+ }
189
+ }
190
+ catch (Exception ex)
191
+ {
192
+ Console.WriteLine(ex.ToString());
193
+ }
194
+
195
+ }
196
+ }
197
+
@@ -0,0 +1,5 @@
1
+ @echo off
2
+ set SCRIPT={script} REM to be replaced by JSCompiler
3
+
4
+ set DIR=%~dp0
5
+ "%DIR%NodalisPLC.exe" %SCRIPT%
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ SCRIPT="{script}" # to be replaced by JSCompiler
3
+
4
+ DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ "$DIR/NodalisPLC" "$SCRIPT"