nodalis-compiler 1.0.26 → 1.0.28
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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/src/compilers/ArduinoCompiler.js +45 -1
- package/src/compilers/support/arduino/gpio.cpp +1 -0
- package/src/compilers/support/arduino/modbus.cpp +96 -19
- package/src/compilers/support/arduino/modbus.h +1 -1
- package/src/compilers/support/arduino/network_config.cpp +502 -0
- package/src/compilers/support/arduino/network_config.h +20 -0
- package/src/compilers/support/arduino/nodalis.cpp +119 -15
- package/src/compilers/support/arduino/nodalis.h +10 -0
- package/src/compilers/support/generic/gpio.cpp +9 -0
- package/src/compilers/support/generic/gpio.h +1 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/NodalisEngine.cs +0 -1
- package/src/compilers/support/jint/nodalis/NodalisEngine/NodalisEngine.csproj +1 -1
- package/src/compilers/support/jint/nodalis/NodalisEngine/bin/Release/NodalisEngine.1.0.10.nupkg +0 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/bin/Release/net8.0/NodalisEngine.deps.json +24 -24
- package/src/compilers/support/jint/nodalis/NodalisEngine/bin/Release/net8.0/NodalisEngine.dll +0 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/bin/Release/net8.0/NodalisEngine.pdb +0 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/bin/Release/net8.0/NodalisEngine.xml +0 -40
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/NodalisEngine.csproj.nuget.dgspec.json +1 -1
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/NodalisEngine.csproj.nuget.g.targets +1 -1
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/NodalisEngine.1.0.10.nuspec +31 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/NodalisEngine.AssemblyInfo.cs +3 -3
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/NodalisEngine.AssemblyInfoInputs.cache +1 -1
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/NodalisEngine.assets.cache +0 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/NodalisEngine.csproj.AssemblyReference.cache +0 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/NodalisEngine.csproj.CoreCompileInputs.cache +1 -1
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/NodalisEngine.dll +0 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/NodalisEngine.pdb +0 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/NodalisEngine.sourcelink.json +1 -1
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/NodalisEngine.xml +0 -40
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/ref/NodalisEngine.dll +0 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/Release/net8.0/refint/NodalisEngine.dll +0 -0
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/project.assets.json +18 -18
- package/src/compilers/support/jint/nodalis/NodalisEngine/obj/project.nuget.cache +4 -4
- package/src/compilers/support/nodejs/gpio.js +3 -0
- package/src/programmers/ArduinoProgrammer.js +37 -1
- package/src/programmers/SSHProgrammer.js +147 -10
- package/src/programmers/utils.js +27 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.0.28] 2026-03-23
|
|
4
|
+
- Improved support for Arduino, fully tested Arduino compile -> program stack.
|
|
5
|
+
- Added a command line interface for nodalis on Arduino to use in setting IP, read/write of bits, and map info.
|
|
6
|
+
|
|
7
|
+
## [1.0.27] 2026-03-20
|
|
8
|
+
- Corrected issues with SSH Programmer.
|
|
9
|
+
|
|
2
10
|
## [1.0.26] 2026-03-19
|
|
3
11
|
- Improved error handling on programming.
|
|
4
12
|
|
package/package.json
CHANGED
|
@@ -219,20 +219,62 @@ export class ArduinoCompiler extends Compiler {
|
|
|
219
219
|
|
|
220
220
|
const inoCode = `#include "nodalis.h"
|
|
221
221
|
#include <stdint.h>
|
|
222
|
+
#include <Ethernet.h>
|
|
222
223
|
#include "modbus.h"
|
|
224
|
+
#include "network_config.h"
|
|
223
225
|
|
|
224
226
|
NodalisModbusTcpServer modbusServer;
|
|
227
|
+
IPAddress localIp;
|
|
228
|
+
|
|
229
|
+
#if defined(LEDR)
|
|
230
|
+
const int NODALIS_HEARTBEAT_LED_PIN = LEDR;
|
|
231
|
+
#elif defined(LED_BUILTIN)
|
|
232
|
+
const int NODALIS_HEARTBEAT_LED_PIN = LED_BUILTIN;
|
|
233
|
+
#else
|
|
234
|
+
const int NODALIS_HEARTBEAT_LED_PIN = -1;
|
|
235
|
+
#endif
|
|
236
|
+
|
|
237
|
+
void nodalisHeartbeatTask() {
|
|
238
|
+
if (NODALIS_HEARTBEAT_LED_PIN < 0) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
static uint64_t lastToggle = 0;
|
|
243
|
+
static bool ledState = false;
|
|
244
|
+
const uint64_t now = elapsed();
|
|
245
|
+
if (now - lastToggle < 500) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
lastToggle = now;
|
|
250
|
+
ledState = !ledState;
|
|
251
|
+
digitalWrite(NODALIS_HEARTBEAT_LED_PIN, ledState ? HIGH : LOW);
|
|
252
|
+
}
|
|
225
253
|
${transpiledCode}
|
|
226
254
|
|
|
227
255
|
void setup() {
|
|
256
|
+
Serial.begin(115200);
|
|
257
|
+
nodalisLogInfo("Setup starting");
|
|
258
|
+
if (NODALIS_HEARTBEAT_LED_PIN >= 0) {
|
|
259
|
+
pinMode(NODALIS_HEARTBEAT_LED_PIN, OUTPUT);
|
|
260
|
+
digitalWrite(NODALIS_HEARTBEAT_LED_PIN, LOW);
|
|
261
|
+
}
|
|
262
|
+
localIp = nodalisLoadIpAddress();
|
|
263
|
+
nodalisBeginEthernet(localIp);
|
|
264
|
+
nodalisLogInfo("Ethernet initialized");
|
|
228
265
|
${globals.join('\n')}
|
|
229
|
-
modbusServer.start()
|
|
266
|
+
if (!modbusServer.start()) {
|
|
267
|
+
nodalisLogError("Modbus server start failed");
|
|
268
|
+
}
|
|
230
269
|
${mapCode}
|
|
270
|
+
nodalisLogInfo("Setup complete");
|
|
231
271
|
}
|
|
232
272
|
|
|
233
273
|
void loop() {
|
|
274
|
+
nodalisPollSerialIpConfig(localIp);
|
|
234
275
|
modbusServer.poll();
|
|
235
276
|
superviseIO();
|
|
277
|
+
nodalisHeartbeatTask();
|
|
236
278
|
${taskCode}
|
|
237
279
|
delay(1);
|
|
238
280
|
PROGRAM_COUNT++;
|
|
@@ -254,6 +296,8 @@ void loop() {
|
|
|
254
296
|
fs.cpSync(path.join(supportDir, 'modbus.cpp'), path.join(outputPath, 'modbus.cpp'), { force: true });
|
|
255
297
|
fs.cpSync(path.join(supportDir, 'gpio.h'), path.join(outputPath, 'gpio.h'), { force: true });
|
|
256
298
|
fs.cpSync(path.join(supportDir, 'gpio.cpp'), path.join(outputPath, 'gpio.cpp'), { force: true });
|
|
299
|
+
fs.cpSync(path.join(supportDir, 'network_config.h'), path.join(outputPath, 'network_config.h'), { force: true });
|
|
300
|
+
fs.cpSync(path.join(supportDir, 'network_config.cpp'), path.join(outputPath, 'network_config.cpp'), { force: true });
|
|
257
301
|
fs.cpSync(path.join(supportDir, 'json.hpp'), path.join(outputPath, 'json.hpp'), { force: true });
|
|
258
302
|
|
|
259
303
|
if (this.isExecutableOutput()) {
|
|
@@ -55,7 +55,7 @@ ModbusResponse NodalisModbusServer::handleRequest(const ModbusRequest &request)
|
|
|
55
55
|
NodalisModbusClient::NodalisModbusClient(const std::string &ip, uint16_t port, uint8_t unitId)
|
|
56
56
|
: IOClient("MODBUS-TCP"),
|
|
57
57
|
ip(ip),
|
|
58
|
-
port(port
|
|
58
|
+
port(port),
|
|
59
59
|
modbusTcpClient(tcpClient),
|
|
60
60
|
deviceAddress(unitId == 0 ? 1 : unitId),
|
|
61
61
|
serverResolved(false)
|
|
@@ -63,6 +63,10 @@ NodalisModbusClient::NodalisModbusClient(const std::string &ip, uint16_t port, u
|
|
|
63
63
|
if (!ip.empty())
|
|
64
64
|
{
|
|
65
65
|
serverResolved = parseIp(ip, serverIp);
|
|
66
|
+
if (!serverResolved)
|
|
67
|
+
{
|
|
68
|
+
logErrorThrottled("Invalid remote IP " + ip);
|
|
69
|
+
}
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
72
|
|
|
@@ -109,7 +113,7 @@ void NodalisModbusClient::onMappingAdded(const IOMap &map)
|
|
|
109
113
|
{
|
|
110
114
|
ip = map.moduleID;
|
|
111
115
|
}
|
|
112
|
-
if (
|
|
116
|
+
if (!map.modulePort.empty())
|
|
113
117
|
{
|
|
114
118
|
port = static_cast<uint16_t>(std::atoi(map.modulePort.c_str()));
|
|
115
119
|
}
|
|
@@ -126,6 +130,10 @@ void NodalisModbusClient::connect()
|
|
|
126
130
|
if (!ip.empty() && !serverResolved)
|
|
127
131
|
{
|
|
128
132
|
serverResolved = parseIp(ip, serverIp);
|
|
133
|
+
if (!serverResolved)
|
|
134
|
+
{
|
|
135
|
+
logErrorThrottled("Could not parse target IP " + ip);
|
|
136
|
+
}
|
|
129
137
|
}
|
|
130
138
|
connected = ensureConnected();
|
|
131
139
|
}
|
|
@@ -133,7 +141,7 @@ void NodalisModbusClient::connect()
|
|
|
133
141
|
bool NodalisModbusClient::connectTCP(const std::string &newIp, uint16_t newPort)
|
|
134
142
|
{
|
|
135
143
|
ip = newIp;
|
|
136
|
-
port =
|
|
144
|
+
port = newPort;
|
|
137
145
|
serverResolved = parseIp(ip, serverIp);
|
|
138
146
|
connected = ensureConnected();
|
|
139
147
|
return connected;
|
|
@@ -143,13 +151,20 @@ bool NodalisModbusClient::ensureConnected()
|
|
|
143
151
|
{
|
|
144
152
|
if (!serverResolved)
|
|
145
153
|
{
|
|
154
|
+
logErrorThrottled("Server IP is unresolved");
|
|
146
155
|
return false;
|
|
147
156
|
}
|
|
148
157
|
if (modbusTcpClient.connected())
|
|
149
158
|
{
|
|
150
159
|
return true;
|
|
151
160
|
}
|
|
152
|
-
|
|
161
|
+
const uint16_t effectivePort = (port == 0) ? 502 : port;
|
|
162
|
+
const bool started = modbusTcpClient.begin(serverIp, effectivePort) == 1;
|
|
163
|
+
if (!started)
|
|
164
|
+
{
|
|
165
|
+
logErrorThrottled("TCP connect failed to " + ip + ":" + std::to_string(effectivePort));
|
|
166
|
+
}
|
|
167
|
+
return started;
|
|
153
168
|
}
|
|
154
169
|
|
|
155
170
|
void NodalisModbusClient::disconnect()
|
|
@@ -183,15 +198,16 @@ bool NodalisModbusClient::sendRequest(const ModbusRequest &request, ModbusRespon
|
|
|
183
198
|
return false;
|
|
184
199
|
}
|
|
185
200
|
|
|
186
|
-
|
|
201
|
+
bool NodalisModbusClient::parseRemoteAddress(const std::string &remote, uint16_t &address) const
|
|
187
202
|
{
|
|
188
203
|
char *endPtr = nullptr;
|
|
189
204
|
const long parsed = std::strtol(remote.c_str(), &endPtr, 10);
|
|
190
|
-
if (endPtr == remote.c_str() || parsed < 0)
|
|
205
|
+
if (endPtr == remote.c_str() || *endPtr != '\0' || parsed < 0 || parsed > 65535)
|
|
191
206
|
{
|
|
192
|
-
return
|
|
207
|
+
return false;
|
|
193
208
|
}
|
|
194
|
-
|
|
209
|
+
address = static_cast<uint16_t>(parsed & 0xFFFF);
|
|
210
|
+
return true;
|
|
195
211
|
}
|
|
196
212
|
|
|
197
213
|
bool NodalisModbusClient::readBit(const std::string &remote, int &result)
|
|
@@ -200,10 +216,17 @@ bool NodalisModbusClient::readBit(const std::string &remote, int &result)
|
|
|
200
216
|
{
|
|
201
217
|
return false;
|
|
202
218
|
}
|
|
203
|
-
|
|
204
|
-
|
|
219
|
+
uint16_t addr = 0;
|
|
220
|
+
if (!parseRemoteAddress(remote, addr))
|
|
221
|
+
{
|
|
222
|
+
logErrorThrottled("Invalid remote bit address \"" + remote + "\"");
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
const long val = modbusTcpClient.discreteInputRead(deviceAddress, addr);
|
|
205
226
|
if (val < 0)
|
|
206
227
|
{
|
|
228
|
+
logErrorThrottled("Discrete input read failed remote=\"" + remote + "\" parsed=" + std::to_string(addr) +
|
|
229
|
+
" unit=" + std::to_string(deviceAddress) + ": " + modbusTcpClient.lastError());
|
|
207
230
|
return false;
|
|
208
231
|
}
|
|
209
232
|
result = (val == 0) ? 0 : 1;
|
|
@@ -216,8 +239,20 @@ bool NodalisModbusClient::writeBit(const std::string &remote, int value)
|
|
|
216
239
|
{
|
|
217
240
|
return false;
|
|
218
241
|
}
|
|
219
|
-
|
|
220
|
-
|
|
242
|
+
uint16_t addr = 0;
|
|
243
|
+
if (!parseRemoteAddress(remote, addr))
|
|
244
|
+
{
|
|
245
|
+
logErrorThrottled("Invalid remote bit address \"" + remote + "\"");
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
const int rc = modbusTcpClient.coilWrite(deviceAddress, addr, value != 0);
|
|
249
|
+
if (rc != 1)
|
|
250
|
+
{
|
|
251
|
+
logErrorThrottled("Coil write failed remote=\"" + remote + "\" parsed=" + std::to_string(addr) +
|
|
252
|
+
" unit=" + std::to_string(deviceAddress) + ": " + modbusTcpClient.lastError());
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
return true;
|
|
221
256
|
}
|
|
222
257
|
|
|
223
258
|
bool NodalisModbusClient::readByte(const std::string &remote, uint8_t &result)
|
|
@@ -242,10 +277,17 @@ bool NodalisModbusClient::readWord(const std::string &remote, uint16_t &result)
|
|
|
242
277
|
{
|
|
243
278
|
return false;
|
|
244
279
|
}
|
|
245
|
-
|
|
280
|
+
uint16_t addr = 0;
|
|
281
|
+
if (!parseRemoteAddress(remote, addr))
|
|
282
|
+
{
|
|
283
|
+
logErrorThrottled("Invalid remote register address \"" + remote + "\"");
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
246
286
|
const long val = modbusTcpClient.holdingRegisterRead(deviceAddress, addr);
|
|
247
287
|
if (val < 0)
|
|
248
288
|
{
|
|
289
|
+
logErrorThrottled("Holding register read failed remote=\"" + remote + "\" parsed=" + std::to_string(addr) +
|
|
290
|
+
" unit=" + std::to_string(deviceAddress) + ": " + modbusTcpClient.lastError());
|
|
249
291
|
return false;
|
|
250
292
|
}
|
|
251
293
|
result = static_cast<uint16_t>(val & 0xFFFF);
|
|
@@ -258,15 +300,32 @@ bool NodalisModbusClient::writeWord(const std::string &remote, uint16_t value)
|
|
|
258
300
|
{
|
|
259
301
|
return false;
|
|
260
302
|
}
|
|
261
|
-
|
|
262
|
-
|
|
303
|
+
uint16_t addr = 0;
|
|
304
|
+
if (!parseRemoteAddress(remote, addr))
|
|
305
|
+
{
|
|
306
|
+
logErrorThrottled("Invalid remote register address \"" + remote + "\"");
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
const int rc = modbusTcpClient.holdingRegisterWrite(deviceAddress, addr, value);
|
|
310
|
+
if (rc != 1)
|
|
311
|
+
{
|
|
312
|
+
logErrorThrottled("Holding register write failed remote=\"" + remote + "\" parsed=" + std::to_string(addr) +
|
|
313
|
+
" unit=" + std::to_string(deviceAddress) + ": " + modbusTcpClient.lastError());
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
return true;
|
|
263
317
|
}
|
|
264
318
|
|
|
265
319
|
bool NodalisModbusClient::readDWord(const std::string &remote, uint32_t &result)
|
|
266
320
|
{
|
|
267
321
|
uint16_t hi = 0;
|
|
268
322
|
uint16_t lo = 0;
|
|
269
|
-
|
|
323
|
+
uint16_t addr = 0;
|
|
324
|
+
if (!parseRemoteAddress(remote, addr))
|
|
325
|
+
{
|
|
326
|
+
logErrorThrottled("Invalid remote register address \"" + remote + "\"");
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
270
329
|
|
|
271
330
|
if (!readWord(std::to_string(addr), hi))
|
|
272
331
|
{
|
|
@@ -283,7 +342,12 @@ bool NodalisModbusClient::readDWord(const std::string &remote, uint32_t &result)
|
|
|
283
342
|
|
|
284
343
|
bool NodalisModbusClient::writeDWord(const std::string &remote, uint32_t value)
|
|
285
344
|
{
|
|
286
|
-
|
|
345
|
+
uint16_t addr = 0;
|
|
346
|
+
if (!parseRemoteAddress(remote, addr))
|
|
347
|
+
{
|
|
348
|
+
logErrorThrottled("Invalid remote register address \"" + remote + "\"");
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
287
351
|
const uint16_t hi = static_cast<uint16_t>((value >> 16) & 0xFFFF);
|
|
288
352
|
const uint16_t lo = static_cast<uint16_t>(value & 0xFFFF);
|
|
289
353
|
|
|
@@ -296,7 +360,12 @@ bool NodalisModbusClient::writeDWord(const std::string &remote, uint32_t value)
|
|
|
296
360
|
|
|
297
361
|
bool NodalisModbusClient::readLWord(const std::string &remote, uint64_t &result)
|
|
298
362
|
{
|
|
299
|
-
|
|
363
|
+
uint16_t addr = 0;
|
|
364
|
+
if (!parseRemoteAddress(remote, addr))
|
|
365
|
+
{
|
|
366
|
+
logErrorThrottled("Invalid remote register address \"" + remote + "\"");
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
300
369
|
uint16_t regs[4] = {0, 0, 0, 0};
|
|
301
370
|
|
|
302
371
|
for (uint16_t i = 0; i < 4; ++i)
|
|
@@ -316,7 +385,12 @@ bool NodalisModbusClient::readLWord(const std::string &remote, uint64_t &result)
|
|
|
316
385
|
|
|
317
386
|
bool NodalisModbusClient::writeLWord(const std::string &remote, uint64_t value)
|
|
318
387
|
{
|
|
319
|
-
|
|
388
|
+
uint16_t addr = 0;
|
|
389
|
+
if (!parseRemoteAddress(remote, addr))
|
|
390
|
+
{
|
|
391
|
+
logErrorThrottled("Invalid remote register address \"" + remote + "\"");
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
320
394
|
const uint16_t regs[4] = {
|
|
321
395
|
static_cast<uint16_t>((value >> 48) & 0xFFFF),
|
|
322
396
|
static_cast<uint16_t>((value >> 32) & 0xFFFF),
|
|
@@ -404,6 +478,7 @@ bool NodalisModbusTcpServer::start(uint8_t serverId)
|
|
|
404
478
|
|
|
405
479
|
if (modbusServer.begin(serverId) != 1)
|
|
406
480
|
{
|
|
481
|
+
nodalisLogError("Failed to start Modbus TCP server");
|
|
407
482
|
return false;
|
|
408
483
|
}
|
|
409
484
|
|
|
@@ -418,6 +493,7 @@ bool NodalisModbusTcpServer::start(uint8_t serverId)
|
|
|
418
493
|
|
|
419
494
|
tcpServer.begin();
|
|
420
495
|
started = true;
|
|
496
|
+
nodalisLogInfo("Modbus TCP server started");
|
|
421
497
|
|
|
422
498
|
for (size_t i = 0; i < globals.size(); ++i)
|
|
423
499
|
{
|
|
@@ -441,6 +517,7 @@ void NodalisModbusTcpServer::poll()
|
|
|
441
517
|
EthernetClient client = tcpServer.available();
|
|
442
518
|
if (client)
|
|
443
519
|
{
|
|
520
|
+
nodalisLogInfo("Accepted Modbus TCP client");
|
|
444
521
|
modbusServer.accept(client);
|
|
445
522
|
}
|
|
446
523
|
modbusServer.poll();
|
|
@@ -108,7 +108,7 @@ private:
|
|
|
108
108
|
uint8_t deviceAddress;
|
|
109
109
|
bool serverResolved;
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
bool parseRemoteAddress(const std::string &remote, uint16_t &address) const;
|
|
112
112
|
uint8_t parseUnitId(const IOMap &map) const;
|
|
113
113
|
bool ensureConnected();
|
|
114
114
|
bool parseIp(const std::string &value, IPAddress &out) const;
|