-
Notifications
You must be signed in to change notification settings - Fork 504
Expand file tree
/
Copy pathRELEASE_NOTES
More file actions
1661 lines (1473 loc) · 79.5 KB
/
Copy pathRELEASE_NOTES
File metadata and controls
1661 lines (1473 loc) · 79.5 KB
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
==============================================================
(Unreleased) Apache PLC4X 1.1.0-SNAPSHOT
==============================================================
New Features
------------
Incompatible changes
--------------------
Bug Fixes
---------
==============================================================
Apache PLC4X 1.0.0
==============================================================
New Features
------------
- Java Configurations of drivers now support a "FILE" type
of configuration parameter.
- Subscription of change-of-state values now supports
providing a min time interval to prevent excessive
notifications.
- Added a new PlcCertificateAuthentication to the API module.
- Initial version of a new Java UMAS driver.
- Initial version of a new Java SLMP (Mitsubishi MELSEC) driver:
read and write access to word devices (D/W/R) using binary 3E
frames over TCP (including Batch Write).
- The 'plc4x' proxy driver now supports TLS as a transport and
requires mandatory username/password authentication
(configured via the new "username" and "password" connection
parameters).
- The Go serial transport now supports the full set of serial
options in the connection string: data-bits, stop-bits, parity,
flow-control, dtr, rts, read-timeout and write-timeout.
- The Go serial transport supports sharing one physical serial port
between multiple connections ("reuse-port", e.g. multi-slave Modbus
RTU) with broadcast reads and serialized writes, plus inter-frame
write pacing ("interframe-delay") on both shared and dedicated ports.
- The Java serial transport's shared-port mode ("reuse-port") now uses a
single broadcast reader per physical port, fixing responses being split
between connections, and "interframe-delay" write pacing works on both
shared and dedicated ports (gap measured from the last write or
received data).
- The Java Modbus RTU codec now frames responses by function code with
CRC validation and byte-wise resynchronization: batched deliveries
(e.g. on shared serial ports) yield every frame instead of only the
first, and partial or corrupted frames no longer discard buffered data.
- Java Modbus serial connections (RTU/ASCII) now serialize their requests
per connection (single outstanding transaction, matching the protocol),
fixing concurrent same-unit requests receiving each other's responses;
unknown Modbus exception codes map to REMOTE_ERROR instead of failing;
shared serial ports dispatch each connection's callbacks on their own
thread, so one blocked callback no longer stalls the whole port.
Responses are additionally validated against the pending request's
function code (late responses from timed-out requests are discarded
instead of completing the wrong caller), and the request timeout now
covers the full time from submission, including queueing.
- Drivers that don't natively support subscriptions (Modbus,
EtherNet/IP, AB-ETH, SLMP, UMAS) now provide subscriptions
through a polling-based emulation layer, supporting CYCLIC and
CHANGE_OF_STATE subscriptions.
- Added a new remote-data-fetching component (event-pump)
replacing the old scraper.
- Re-implemented the connection-cache with more reliable resource
handling and transparent re-subscription after a connection was
lost and re-established.
- New TCP transport implementation using per-connection
virtual-thread blocking I/O (replacing the NIO selector),
scaling better on Java 21.
- OPC-UA: Added reading and writing of structured values
(PlcStruct) and corrected array handling.
- OPC-UA: Tag data types are now derived from the server's type
model instead of being guessed from the input data.
- OPC-UA: Added browse support, resolving server-side types and
access rights.
- Simulated driver: Added STRING support.
- CANopen: Added NMT command support.
- Go: Added a production-grade BACnet/IP driver (segmentation,
write priority, directed/multi-target WhoIs, routed addressing,
array/bit-string property decoding).
- Go: The connection-cache now supports a configurable max idle
time, and connections carrying subscriptions are exempt from the
idle TTL.
- The Object-PLC-Mapping (OPM) module was ported to SPI3.
- PlcBrowseItem now reports which subscription types each item
supports via a new getSupportedSubscriptionTypes() method, so
browse results carry subscription capability information.
- OPC-UA: Improved Siemens S7-1500 support, including reading and
writing of (almost) all variant types.
- Updated the bundled KNX manufacturer data and the BACnet/KNX
vendor IDs for broader device recognition.
- Go: Connections can now be invalidated (Invalidate()) to mark
themselves irrecoverably failed for lease management, and
transport errors are now classified and propagated through
codecs/transports (TransportErrorKind).
- Performance: Sped up byte-aligned integer read/write in the Java
byte-based SPI buffers.
- EtherNet/IP: The CIP bit-string types (BYTE, WORD, DWORD, LWORD)
and unsigned integer types (USINT, UINT, UDINT, ULINT) can now be
read and written, in both the Java and the Go drivers. They were
accepted by the address parser but not decoded, so reading such a
tag returned INTERNAL_ERROR. All eight are unsigned over their
full range: a DWORD reads as 0 to 4294967295 and an LWORD as 0 to
18446744073709551615. Their signed counterparts are unchanged - a
DINT of 0xFFFFFFFF still reads as -1.
- Modbus: STRING and WSTRING values can now be read and written, in
PLC4J, PLC4Go and PLC4C. The length of one string is written in
parentheses the way the S7 driver spells it, so
"holding-register:1[0..2]:STRING(20)" is three 20-character
strings.
- OPC-UA: A new "subscription-queue-size" parameter (default 1) keeps
the values a server queues between publishes. With a depth above
one, change-of-state items are sampled at the server's fastest rate
and every queued value is delivered instead of only the last. At the
default nothing changes, and event and cyclic items keep a queue of
one either way.
- OPC-UA: A new "min-channel-lifetime-ms" parameter (default 5000)
bounds a channel lifetime the server revises downwards. Channel
renewals run on one executor shared by every OPC UA connection in
the JVM, so a single server answering with a very short - or zero -
lifetime used to set the pace for all of them.
- OPC-UA: Added authentication with an X509 user certificate
(GH-1845).
- EtherNet/IP: The driver falls back to "Get Attribute Single" where a
device does not answer "Get Attribute All", so the Connection
Manager and the Message Router are detected on devices that only
support the single-attribute form.
- S7: A connection reports what the device says about itself.
"readDeviceIdentification()" reads the module type, order number,
serial number, firmware version and protection level out of the SZL
lists, following a list the CPU splits across several PDUs.
- The event-pump accepts a trigger interval in milliseconds
("intervalMillis" / "initialDelayMillis") beside the existing
whole-second form. Giving the same setting in both units fails at
startup rather than silently picking one.
- Go: PLC4Go gained five drivers - AB-Ethernet, Firmata,
IEC 60870-5-104, SLMP (MELSEC) and UMAS - taking it from nine
drivers to fourteen. AB-Ethernet and Firmata are as partial as they
are in Java (one does not write, the other does not read),
IEC 60870-5-104 subscribes and nothing else because the protocol is
push driven, and SLMP addresses the word devices D, W and R.
- Go: The EtherNet/IP driver was brought up to the Java driver's
level: all three read and write paths (sequential, message-router
and connected), the plc4j connect handshake, UDP broadcast discovery
(ListIdentity) over per-interface sockets, a "logix" driver alias
forcing little-endian encapsulation, and the "bigEndian",
"forceUnconnectedOperation", "communicationPath" and
"connectionSerialNumber" options.
- Go: The S7 driver gained browse (static areas and DB block
enumeration), alarm and cyclic subscriptions with alarm query and
push dispatch, a real round-trip ping, and parsing of S5TIME,
variable-length strings and alarm tag addresses.
- Go: Modbus RTU and Modbus ASCII have codecs of their own, including
the RTU CRC and the ASCII LRC, and the tag, value and configuration
gaps to the Java driver are closed.
- Go: The KNXnet/IP driver writes group addresses, and its
subscriptions work.
- Go: A polling-subscription base is part of the default connection
set, so a driver whose protocol has no subscriptions can offer them
the way the Java drivers do.
- Go: PlcDATE and PlcTIME_OF_DAY expose their components rather than
only the whole value.
- Go: Every timeout is named (utils.WithNamedTimeout), so an expiry
says which timeout it was.
- The API's "Option" type reports whether a configuration option
carries a secret ("isSecret()"), which is what redaction now asks
instead of guessing from the parameter's name. It is a default
method returning false, so existing implementations keep compiling.
Incompatible changes
--------------------
- Configuration parameters now use one vocabulary across PLC4J and PLC4Go.
A duration in milliseconds ends in "-ms", TLS settings live under "tls.",
and a parameter aimed at a transport no longer repeats that transport's
code. Old names are removed rather than deprecated: supplying one is
reported as an unknown parameter, naming the replacement, and the setting
does not apply. The full table is below.
Durations:
request-timeout -> request-timeout-ms
timeout-request (ads) -> request-timeout-ms
connect-timeout -> connect-timeout-ms
read-timeout -> read-timeout-ms
write-timeout -> write-timeout-ms
session-timeout -> session-timeout-ms
channel-lifetime -> channel-lifetime-ms
min-channel-lifetime -> min-channel-lifetime-ms
ha-heartbeat-interval -> ha-heartbeat-interval-ms
ha-failover-timeout -> ha-failover-timeout-ms
Establishing a socket and completing a protocol handshake are two
settings, not one, so they now have two names. "connect-timeout-ms" is
the socket connect; the COTP handshake and the OPC UA negotiation steps
are "handshake-timeout-ms":
cotp.cotp-connection-timeout -> cotp.handshake-timeout-ms
negotiation-timeout (opcua) -> handshake-timeout-ms
Transport parameters no longer repeat their transport's code, which the
prefix already supplies:
tcp.tcp-no-delay -> tcp.no-delay
cotp.cotp-tpdu-size -> cotp.tpdu-size
tls.tls-version -> tls.version
TLS settings are addressed under "tls.":
tls.verify-ssl -> tls.verify
key-store-file (opcua) -> tls.keystore
key-store-password -> tls.keystore-password
key-store-type -> tls.keystore-type
trust-store-file -> tls.trust-store
trust-store-password -> tls.trust-store-password
trust-store-type -> tls.trust-store-type
The trust store drops "-file" for the same reason the key store does:
every one of these names a store, so saying so adds nothing. The TLS
transport already spelled them "tls.trust-store-file"; that becomes
"tls.trust-store" too, so the opcua and ctrlx drivers, which declare
their own, now agree with it.
A name a protocol specification fixes keeps its own spelling and units:
SLMP's "monitoring-timer" is a field of the 3E request frame in the
protocol's own units, not a value in milliseconds, so it is unchanged and
carries a comment at its declaration saying why.
- The OPC UA driver's "insecure-certificate-verification" became
"tls.verify", with the opposite sense. A connection that set
"insecure-certificate-verification=true" must now set "tls.verify=false".
This one is not just a rename: if it is missed, the new default applies,
which is to verify the server certificate. That fails loudly against a
server whose certificate does not validate rather than connecting
insecurely, but it is a behaviour change and not a silent one.
- An unrecognised connection-string parameter is now reported in PLC4Go as
well as PLC4J, naming the parameter and, where it can, the nearest known
one. It remains a warning: a stray parameter does not fail a connection
that would otherwise work. PLC4Go's OPC UA driver previously *refused*
the connection on an unknown option; it now warns like every other
driver, so a connection string accepted by PLC4J is no longer rejected
there.
In PLC4Go this covers the drivers that parse their configuration in one
place: ab-eth, bacnet-ip, c-bus, EtherNet/IP, firmata, IEC 60870-5-104,
Modbus, OPC UA, S7, SLMP and UMAS. The ADS, KNXnet/IP and simulated
drivers read their options where they are used rather than parsing a
configuration, so there is no point at which the leftovers are known;
they are unchanged and still report nothing.
The report also knows which transport the connection actually uses, so a
parameter that belongs to a different transport - "serial.baud-rate" on a
TCP connection - is called out as misdirected instead of being silently
excused as "some transport's".
A suggestion is offered only among the names the consumer that reported
actually read, so a parameter belonging to a transport is named as
unknown with nothing to suggest. PLC4J does better here: it draws the
known names from the driver, the transport, the audit log and the
connection-control options, and matches on the last segment, so a
missing prefix is recognised for what it is.
- Configuration values carrying secrets are marked at their declaration -
"@Secret" in PLC4J, a `secret:"true"` struct tag in PLC4Go - and render
as "<redacted>" wherever a configuration is rendered. This replaces
guessing from parameter names, which could only ever be one parameter
behind: a pre-shared key was logged in clear until its name was added to
the list by hand. A name-based check remains for parameters no
configuration declares, since a credential passed under an unknown name
is still a credential.
- PLC4Go's S7 driver reads the rack and slot as "cotp.local-rack",
"cotp.local-slot", "cotp.remote-rack" and "cotp.remote-slot". It read
them unprefixed, while PLC4J declares them on the COTP transport's
configuration and every S7 example in the documentation spells them with
the prefix - so the documented connection string set nothing in PLC4Go
and said so nowhere. The unprefixed names are now reported as unknown.
- Fixed PLC4Go logging connection strings verbatim. A password in a Go
connection string reached the log in clear at debug level, at twenty
call sites across the driver manager and the connection cache. They are
redacted now, along with credentials in a URI's userinfo. The parsed
URL and the connection container render redacted too - both reached the
same log lines by another route, so a redacted field sat beside the
credential it was hiding.
- PLC4Go addresses a transport's connection-string options under the
transport's own code, as PLC4J does and as the documentation has always
said: "tcp.connect-timeout-ms", "serial.baud-rate", "udp.so-reuse",
"pcap.speed-factor". They were read unprefixed, so every documented
transport setting was ignored in PLC4Go and left at its default. The
unprefixed names are now reported as unknown rather than silently
doing nothing. Options a driver injects into the map itself
("defaultTcpPort") are not addressed by anyone and keep their bare
names.
- PLC4Go's OPC UA driver reads the parameter names PLC4J declares and the
documentation lists - "tls.keystore", "tls.keystore-password",
"security-policy", "allow-unverified-security-policies" - rather than
names derived from its own Go struct fields ("keyStoreFile",
"securityPolicy"). The documented connection string reached it as a
set of unknown options and was ignored.
- A secret marking in PLC4Go applies whatever the field's type is. The
generator honoured "secret:\"true\"" only where it rendered a string, so
the tag on any other kind of field was accepted and silently did
nothing. The OPC UA key pair now carries the marking in both the
configuration and the secure channel.
- Redaction decides from the parameter name the driver will read, not the
name as written: "?%70assword=hunter2" is the password parameter once
the query is decoded, and was previously logged in clear. A connection
string nested inside another (the PLC4X proxy driver's
"remote-connection-string") is redacted as a connection string in its
own right, so its credentials no longer travel through the outer one -
while which PLC the proxy talks to stays visible.
- A BACnet/IP connection reported each unknown option once rather than
twice. Its options are parsed both by the driver, for the discovery
timeout, and by the connection; both reported, so one mistake read as
two.
- The connection-creating methods of the API moved from
"PlcConnectionManager" to a new "PlcConnectionFactory"
interface, which the "PlcDriverManager" hands out via
"getConnectionFactory()" (formerly "getConnectionManager()").
"PlcConnectionManager" now extends "PlcConnectionFactory" and
adds the "close()" method, and is only implemented by managers
that keep the connections they hand out, such as the connection
cache.
- The connection cache was renamed from
"CachedPlcConnectionManager" to "PlcConnectionCache", matching
the name the concept already has in PLC4Go. Its builder method
"withConnectionManager()" became "withConnectionFactory()", and
"PlcConnectionManagerClosedException" became
"PlcConnectionCacheClosedException". The Maven artifactId
("plc4j-tools-connection-cache") and the package are
unchanged.
- All drivers were migrated to a new shared SPI ("SPI3"):
dependency-free Read/Write buffers, an updated code-generation
framework, a pluggable-transport system and a layered
protocol-driver model.
- Drivers now reject connection strings using an unsupported
transport with an exception. The check can be force-disabled
via a configuration parameter.
- Several Maven artifactIds changed (tools, transports, scraper);
see the "Changed Maven Coordinates" section below for the full
mapping. Consumers must update their coordinates.
- The PlcBrowseItem interface gained a
getSupportedSubscriptionTypes() method that custom
implementations must now provide.
- Dropped support for Java 11, new baseline Java version is
Java 21.
- Migrated the build to Apache Maven 4.
- The Go serial transport's default baud-rate changed from 115200
to 9600 (aligning with common serial defaults and the Java
transport). Specify baud-rate explicitly if you relied on the
previous default.
- Go serial reads/writes without an explicit context deadline are
now bounded by the new read-timeout/write-timeout options
(default 1000 ms; set to 0 for the previous blocking behavior),
and invalid serial option values now fail connection creation
instead of being silently ignored.
- The Java serial transport removed the unused options
"break-enabled", "receive-buffer-size" and "send-buffer-size",
removed the combined "RTS_CTS_XON_XOFF" flow-control mode, and now
rejects invalid parity/flow-control values instead of silently
falling back to defaults. Option values are case-insensitive and
accept "-" or "_" as separator (canonical forms: none, odd, even,
mark, space; none, rts-cts, xon-xoff).
- Acquiring a shared Java serial port ("reuse-port") with a configuration
differing from the first connection's now fails with an error instead
of silently reusing the first configuration.
- The 'plc4x' proxy driver now defaults to the TLS transport
instead of plaintext TCP. Existing plaintext connections must
switch to an explicit transport prefix (e.g. "plc4x:tcp://...").
When using TLS against a server with a self-signed certificate,
set "tls.verify-ssl=false" (or pin the certificate).
- The 'plc4x' proxy driver now requires username/password
authentication on connect; connecting without credentials, or
with invalid ones, is rejected with an ACCESS_DENIED handshake.
- The PLC4J-API module however is intentionally held at
Java 17 to allow alternate driver implementations to support
Java 17.
- Updated the signature of the PlcBrowseRequestInterceptor to
also accept a queryName, query and item instead of just an
item.
- The ConnectionStateListener interface was updated to no longer
have a connected() and disconnected() method, but use a
onConnectionStateChanged method instead that accepts
PlcConnectionStateChangedEvent events which have many more
state change options beyond a simple connected and disconnected
event.
- The OPC UA driver's "security-policy" now defaults to
Basic256Sha256 instead of NONE. NONE means the channel is
neither signed nor encrypted, so anything on the network path
can read and alter what is exchanged, and the server is not
authenticated at all.
A protected channel needs the server's certificate to be known
before the channel is opened. The discovery phase that would
otherwise fetch it runs unprotected by necessity, and a
certificate learned from the peer it is meant to authenticate
establishes nothing - so the driver no longer proceeds from
discovery onto a channel weaker than the one configured. It used
to do exactly that, silently: a connection asking for
Basic256Sha256 got a session with neither signing nor encryption
and no indication of it.
Together this means a connection that names no certificate now
fails where it previously came up unprotected. Name one with
"server-certificate-file", or a trust store with
"tls.trust-store"; or set "discovery=false" if the endpoint needs
no discovery; or ask for "security-policy=NONE" to accept an
unprotected channel as before.
Note that a protected channel also needs a client key pair:
supply one with "key-store-file", or the driver generates a
throwaway self-signed certificate, which a server that
authenticates its clients will not accept.
- The OPC UA driver refuses to send a username and password over a
channel that neither signs nor encrypts, because the password is
then readable by anything on the path and stays useful long after
it is read. The new "allow-insecure-credentials" parameter sends
them anyway, with a warning.
- The OPC UA driver now requires an endpoint to match both the
requested security policy and the requested message security
mode, and selects the strongest endpoint that matches rather than
the weakest. It previously accepted an endpoint matching either
one and then chose the lowest security level on offer, so a
server publishing a wide-open endpoint beside a protected one was
usually reached over the wide-open one. Where two endpoints are
equally strong, one whose user token policy protects the token is
preferred.
- The ctrlX driver no longer trusts the Bosch factory default
certificate that ships inside the driver jar, and no longer
accepts a certificate for any host regardless of the name it was
issued for. It also asks for a TLS context rather than the
legacy "SSL" one. That certificate identifies nobody - anything
presenting it and its key was trusted - and because it was the
only trust anchor, a device carrying a properly issued
certificate could not be reached at all. The credentials for the
connection travel over that channel.
Connections to a device still on its factory certificate will
now fail. The new "allow-factory-default-certificate" parameter
restores the old behaviour, with a warning; alternatively
"server-certificate-file" names a single PEM certificate to
trust, or "tls.trust-store" (with "tls.trust-store-password" and
"tls.trust-store-type") a key store of them, matching the names used
by the OPC UA driver and the TLS transport.
"ignore-common-name" is also available if the certificate is
trusted but names a different host.
- The TLS transport now checks that the server's certificate was
issued for the host it connected to. The "ignore-common-name"
parameter was declared but never consulted, so this check did
not happen at all: with "verify-ssl" on, any certificate from a
trusted issuer was accepted for any host, which is what a
machine in the middle needs. Setting "ignore-common-name=true"
restores the old behaviour and logs a warning saying so.
A connection to a device whose certificate names something other
than the address it is reached at will now fail where it
previously succeeded.
Two new parameters make the check usable where a device carries
its own certificate: "tls.trust-store" (with
"tls.trust-store-password" and "tls.trust-store-type") names the
certificates to trust instead of the public authorities the JVM
ships with. Previously the only way past a private CA was
"verify-ssl=false", which turns off both the chain check and
this one.
- The IEC 60870-5-104 driver now derives its S-format
acknowledgements from the send sequence number of the frames it
received, counted modulo 2^15 and encoded in the upper fifteen
bits of the control field, as the standard requires. It
previously sent the received frame's receive sequence number
plus one, which is the station reporting how much of our traffic
it had taken in - a different number, and one that said nothing
about how far we had read. This changes what the driver puts on
the wire in response to received telemetry, so a station that
checks acknowledgements will see different (correct) values, and
one that had adapted to the old behaviour may need attention.
The frame format itself is unchanged.
- Tag addresses naming an implausible number of elements are now
rejected as invalid addresses rather than acted on. An element
count is a request to allocate, and it was taken at face value:
in the s7 driver a count is now measured against the 2097151
bytes an S7 address can reach (for the string forms, against
what one string of the declared length costs, which is the same
arithmetic the optimizer does in an int and where an unbounded
count overflowed); in the ads driver an index group, index
offset and element count must each fit the four bytes ADS
carries them in; in the firmata driver a digital tag must stay
within the 256 pins the protocol can name, having previously
turned its count into that many set bits at parse time. In all
three, a count too wide to be a number used to escape as a
NumberFormatException instead of the PlcInvalidTagException the
tag parsers promise.
- The OPC UA driver bounds a browse, which previously walked
whatever tree the server described for as long as it described
one. "browse-max-references-per-node" (default 65536) limits the
references collected for one node and is now also asked of the
server, "browse-max-total-nodes" (default 1000000) limits how
many nodes one browse expands, and "browse-max-depth" (default
64) limits how deep it recurses. Set any of them to 0 for no
limit. Reaching one warns and returns what was found rather
than failing, so an address space within these sizes is
unaffected.
- The ctrlX driver bounds a browse the same way, with
"browse-max-total-nodes" (default 1000000) and
"browse-max-depth" (default 64). Its browse also now always
completes its future: a node answering with no child list used
to throw inside the executor's task, where nothing caught it and
the caller was left waiting on a future that was never
completed. Such a node is now read as the leaf it says it is.
- The simulated driver bounds how much data one tag may ask it to
make up. The count in the address was multiplied by the size of
one element in an int to size the array it fills, so a large
enough count asked for a negative array rather than a large one,
and anything below that got what it asked for - three hundred
million doubles being 2.4 GB. The product is now measured
against a budget of 16 MiB per tag.
The Go driver carries that count as a uint16, where a count
above it used to be truncated to its low two bytes rather than
refused - a request for 70000 elements handing back a tag of
4464, and one for 65536 handing back a tag of none. Both are
now invalid addresses, as is a count of zero, which the Java
driver already refused.
- The modbus, profinet and profinet-ng tag parsers now report a
count, address or slot too wide to be a number as an invalid
tag. All three already bounded these values, but the checks ran
after the number was read, so anything too wide to read left as
a NumberFormatException instead. The digit widths are capped in
the address patterns, at what each field can hold.
- Every generated parser now refuses a message that nests its
types deeper than 1024 levels, in both the Java and the Go
bindings. Several types contain themselves - BACnet constructed
data holds further constructed data, an OPC UA variant of type
24 holds further variants - so the depth of the value tree is
the sender's to choose and one level costs a single byte on the
wire. Deep enough, that ran the parser out of stack: in Java a
StackOverflowError, which is neither a parse failure the driver
can report nor an error the receive path contains, so a frame
that should have cost one frame ended the connection; in Go a
goroutine out of stack takes the process with it rather than
the request. Set PLC4X_MAX_NESTING_DEPTH for a device whose
messages genuinely nest deeper - it means the same thing in
both bindings, and a value that is not a positive number leaves
the default in place with a warning. The deepest message in the
project's own testsuites nests 36 levels, so this bounds only
what no real device sends. Note for Go consumers that the
default rose from 255 to 1024 in the process, so one setting of
the variable now means one depth whichever binding reads it.
- plc4go now requires Go 1.27. The uuid package it used to take
from github.com/google/uuid now comes from the standard library,
which drops that dependency outright - it is gone from go.mod and
go.sum - at the cost of the version floor.
- EtherNet/IP: Two CIP data type codes were wrong and are
corrected. LWORD moves from 0x00D3 to 0x00D4 and STRINGI from
0x00DD to 0x00DE. Each had been sharing its value with another
type (DWORD and ENGUNIT respectively), and a duplicate key is
silently dropped from the generated lookup tables, so LWORD and
STRINGI could not be resolved by name or by value at all. Anyone
who hardcoded the old LWORD value was addressing a DWORD.
- EtherNet/IP: A string write now emits the structure the read path
parses - a 2-byte structure handle, a 4-byte length and then the
characters - so what the driver writes reads back as the same
value. Previously it wrote a bare length followed by the
characters, which no read could decode. The length is now the
number of UTF-8 bytes rather than of characters; the two differ
for any non-ASCII text, which used to read back truncated. Text
that does not fit the type's fixed payload is reported instead of
overflowing. Because CIPDataTypeCode.STRING declares a size of 0,
the serializer emits no payload for it at all, so a write
addressed as ":STRING" is now rejected rather than silently sent
empty - write strings as ":STRUCTURED".
- Go: Several values now serialize under their own names and in
their canonical forms, which changes the output anything parsing
it will see. PlcDWORD, PlcSINT, PlcULINT and PlcWSTRING were
serialized as PlcDINT, PlcINT, PlcUINT and PlcSTRING; PlcTIME and
PlcLTIME render ISO-8601 with hours/minutes/seconds and a
sub-second fraction instead of truncating to whole seconds;
PlcDATE_AND_TIME renders the UTC wall time in ISO-8601 rather
than Go's local-zone default; and PlcStruct keeps a deterministic
member order. String-ish values now carry encoding="UTF-8".
- Java: The unsigned bit-string values WORD, DWORD and LWORD now
serialize as dataType="uint" rather than through the signed
writers, PlcBYTE serializes as a bit string, and PlcTIME renders
as an ISO-8601 string like PlcLTIME. This aligns the Java and Go
renderings of the same value.
- EtherNet/IP: EipTag is now immutable, matching every other
driver's tag in both bindings - it was the only tag class in
plc4j that exposed setters. setType(...) and setElementNb(...)
are gone; give the type and the element count to the
constructor instead. An element count below one is normalised
to one rather than kept, so a tag built as ":INT:0", or through
the (tag, type) constructor which used to leave the count at
zero, now reads one element instead of none.
- All Java drivers now select array elements with one shared notation, written
before the data type: `[n]` for a single element, `[lo..hi]` for an
inclusive range, an optional `;base` for an array the PLC declares as
starting somewhere other than zero, and one bracket per dimension. The
dimensions of one array may also be written comma-separated inside a single
bracket - `[1..2,3..4]` is the same as `[1..2][3..4]` - which is the form
Allen-Bradley and others use; addresses are always rendered back in the
one-bracket-per-dimension form. See the "Addressing arrays" page.
This replaces four incompatible spellings. `[4]` meant "four elements" in
seven tag classes and "the fifth element" in two; it now means one element
everywhere, and a count is written as a range. Addresses in the old form no
longer parse, and the error names the address to write instead - so an
upgrade reports the change rather than quietly returning different data.
The affected forms, by driver:
S7 %DB42:28.0:BYTE[4] -> %DB42:28.0[0..3]:BYTE
S7 (string) %DB1:0:STRING(20)[4] -> %DB1:0[0..3]:STRING(20)
Modbus holding-register:1:INT[4] -> holding-register:1[0..3]:INT
SLMP D100:INT[4] -> D100[0..3]:INT
ADS (direct) 0x4020/0:DINT[4] -> 0x4020/0[0..3]:DINT
EtherNet/IP myArray[0]:DINT:4 -> myArray[0..3]:DINT
Profinet tag:INT[4] -> tag[0..3]:INT
Profinet-NG 1.2.INPUT.0:INT[4] -> 1.2.INPUT.0[0..3]:INT
Simulated RANDOM/foo:INT[4] -> RANDOM/foo[0..3]:INT
OPC-UA addresses are unchanged - its implementation is the one the shared
notation was extracted from - and ADS and UMAS symbolic addresses keep
their existing form while gaining ranges.
- Firmata is the one driver whose addresses change meaning silently. They
carry no data type (`3[4]`), so the brackets did not move and there is
nothing to reject: `3[4]` used to read four pins starting at pin 3 and now
reads one pin, the fifth. Rewrite these as `3[0..3]`.
- An address that selects nothing now asks for the whole value rather than a
single element. For a scalar that is unchanged; for an array it is every
element, on the drivers that can determine the extent from the device
(OPC-UA, ADS, UMAS). The others read one element as before, because their
addresses are memory offsets with no declared array at them.
- A single index and a one-element range are no longer the same thing.
`myTag[4]` selects one element and yields a scalar, while `myTag[4..4]`
yields a list of one. `PlcTag.getArrayInfo()` reports the shape of the
value received - empty for a scalar, one entry per dimension for an array -
so a consumer can tell the two apart without knowing the protocol.
- `ArrayInfo` gains `getBase()` and `isRange()`, both as default methods, so
existing implementations keep compiling. Its javadoc described `[6]` as a
six-element array, which was never what the drivers did and is not what the
notation means.
- EtherNet/IP rejects an array index above 255 while parsing the address; a
CIP MemberID carries a `uint 8`. A range may run past it, since the request
carries a start and a count, but it cannot begin there.
- ADS and UMAS verify a `;base` written in the address against the bounds the
device declares, and report a disagreement. The device is authoritative; a
base that differs means the address was written against a different layout,
which would otherwise read silently shifted data.
- ADS rejects an address that names a member of an array without saying which
element - `MAIN.g_arr.member` on an array `g_arr`. It previously resolved
against the first element and reported the result as though it were the
whole path.
- Selecting array elements over UMAS is reported as UNSUPPORTED rather than
returning the whole variable. The driver has no per-element arithmetic yet;
the address parses, and the refusal is explicit.
- Fixed `getArrayInfo()` reporting one element too many on the ADS direct and
Firmata drivers, whose inclusive bounds were built from the element count
rather than the last index.
- Fixed a direct ADS array selection transferring every element it asked for
and decoding only the first, in both PLC4J and PLC4Go. The size of the
request was multiplied by the element count while the decoder was given no
shape, so `0x4020/0[0..3]:DINT` returned one value for four elements' worth
of bytes - a well-formed answer to a question nobody asked.
- Fixed a symbolic ADS selection being ignored in PLC4Go. `MAIN.arr[1..4]`
resolved like `MAIN.arr`: the whole array, from its original offset. The
selection now moves the read to the first selected element and transfers
only what it spans, across as many dimensions as the address names
(`MAIN.grid[3,1..3]`), and a selection outside what the device declares is
refused rather than approximated.
- Fixed the shape of a partly selected multi-dimensional ADS array in PLC4J.
`MAIN.grid[1..2]` on an `ARRAY [0..9,0..4]` reported two elements rather
than two rows of five: the dimensions the selection did not name were
dropped from the shape while their bytes were still transferred. They are
selected whole, and are part of the value.
- A dimension of an ADS selection written as a bare index now collapses,
where before every named dimension added a level of list. `grid[3,1..3]` is
a list of three, not a list of one list of three, and `grid[3,2]` is a
scalar. This is the same rule the notation states for a single dimension.
- Fixed SLMP reporting a one-element range as a scalar in PLC4J. `D100[4..4]`
now returns a list of one, as `D100[4]` returns a scalar and as PLC4Go's
SLMP driver already did. Its shape came from the element count, which
cannot express the difference.
- PLC4Go now uses the same array notation as PLC4J, so one address means one
thing in either language. The grammar, the rules and the rendering are the
ones described above; the two share a specification rather than code, and
the Go parser is tested against the Java cases directly.
The forms that changed, by driver:
S7 %M100:INT[10] -> %M100[0..9]:INT
S7 (string) %DB69.DBX68:WSTRING[3] -> %DB69.DBX68[0..2]:WSTRING
Modbus holding-register:1:INT[4] -> holding-register:1[0..3]:INT
SLMP D100:INT[4] -> D100[0..3]:INT
EtherNet/IP %rate:DINT:4 -> %rate[0..3]:DINT
Simulated RANDOM/foo:INT[4] -> RANDOM/foo[0..3]:INT
KNXnet/IP 1.2.3#4B1C:UINT[4] -> 1.2.3#4B1C[0..3]:UINT
KNXnet/IP 1.2.3#11/1/1[4] -> 1.2.3#11/1/1[0..3]
Addresses in the old form no longer parse, and the error names the address
to write instead - with two exceptions, below, where the address parses
either way and only its meaning moves.
- Two Go drivers change the meaning of addresses that still parse, so there is
nothing to reject and nothing to warn about at runtime:
* ADS `[n]` was a *count* of n elements and is now the element at index n.
`MAIN.g_arr[3]` read three elements and now reads one. Rewrite as
`MAIN.g_arr[0..2]`. This also means Go and Java ADS now agree about the
same address; they did not before.
* Firmata `[n]` was a run of n pins and is now the pin at index n, exactly
as in PLC4J. `digital:2[3]` read three pins from pin 2 and now reads
pin 5. Rewrite as `digital:2[0..2]`.
- ADS also drops the `[a:b]` start-and-count form, which had no counterpart in
PLC4J. `MAIN.g_arr[2:4]` is written `MAIN.g_arr[2..5]`.
- A count of zero no longer has a spelling. Several Go drivers accepted `[0]`
and rejected it as "quantity must be greater than zero"; a range is written
with the indices it covers, so there is no way to ask for none, and `[0]`
now selects the first element.
- `ArrayInfo` bounds are inclusive in PLC4Go, as they are in PLC4J:
`GetSize()` returns `UpperBound - LowerBound + 1`. They were exclusive,
documented as a deliberate divergence, so `[0..7]` reported eight elements
in Java and seven in Go - the same disagreement about the same address that
this change exists to remove. Code reading `GetUpperBound()` directly must
be revisited.
- `ArrayInfo` gains `GetBase()` and `IsRange()`. Go has no default methods, so
any implementation outside PLC4Go must add them.
- Addresses that a driver rendered back are now spelled the way its parser
reads them. Several never round-tripped: BACnet/IP rendered `:` where the
syntax wants `,`, gave every property a leading `:`, and printed the address
of the pointer holding an array index rather than the index; KNXnet/IP
device addresses rendered `/` where the syntax wants `.`; the ADS direct
form printed its index group as decimal digits behind an `0x` prefix, so
16416 came back as `0x16416` - a different address; and the S7 tag rendered
as "0:INT[8]", naming neither the memory area nor the offset it read.
- Fixed the Go BACnet/IP driver asking for one element fewer than requested
when a read carried an element count, which followed from the bounds
becoming inclusive.
- C-Bus addresses are unchanged. Its brackets carry the arguments of a CAL
command (`recall=[param, count]`), not a selection appended to an address.
- KNXnet/IP group addresses are unchanged. Their brackets hold a set of group
addresses to match (`[1-3,5]`), not an array selection. Only the two device
address forms, which carry a real element count, moved to the new notation.
- BACnet/IP addresses are unchanged. Its bracket is a property array index,
which already meant what the notation says an index means.
- A connection reports the operations its driver actually implements.
ConnectionBase answered "true" to isReadSupported(),
isWriteSupported(), isSubscribeSupported() and isBrowseSupported()
for every driver built on it, whatever that driver implemented, so
the metadata a tool uses to decide what to offer was not worth
reading. In PLC4Go the EtherNet/IP and Modbus connections left
ProvidesSubscribing and ProvidesBrowsing at their zero value,
reporting "false" by accident rather than by decision. Both now
state what they support, so code branching on these flags will see
different - and correct - answers.
- S7: The day-of-week nibble of a DATE_AND_TIME is numbered the way an
S7 numbers it, counting from Sunday as 1 to Saturday as 7. Both
bindings filled it from their date library, which counts from
Monday, so every DATE_AND_TIME written to a PLC carried a day of
week one short. Parsing rotates back, so the value still round-trips
through PlcDATE_AND_TIME, whose getter keeps the ISO-8601 numbering
that KNX DPT 19.001 needs. Only IEC61131_DATE_AND_TIME is affected;
the DTL variant already carried the Siemens numbering.
- Go: PlcDATE_AND_TIME's GetDayOfWeek() returns the numbering PLC4J
returns - 1 for Monday through 7 for Sunday - rather than Go's
time.Weekday, which counts Sunday as 0. A zero there means "no day
given" in KNX DPT 19.001 and is simply invalid for S7.
- Go: A request that runs out of time is reported as a timeout rather
than as an INTERNAL_ERROR. This covers both the driver's own
request timeout and a deadline the caller set on the context it
passed in - the idiomatic way to call this - so code telling a
timeout from a real failure to know whether retrying makes sense
will now see the timeout it was looking for.
Changed Maven Coordinates
-------------------------
The Maven groupId of all modules is unchanged ("org.apache.plc4x").
The following artifactIds changed; consumers must update their
dependency coordinates accordingly:
- The "tools" modules were renamed to the "plc4j-tools-*" pattern:
plc4j-capture-replay -> plc4j-tools-capture-replay
plc4j-connection-cache -> plc4j-tools-connection-cache
plc4j-opm -> plc4j-tools-opm
- The transport modules were renamed from the singular
"plc4j-transport-*" to the plural "plc4j-transports-*":
plc4j-transport-can -> plc4j-transports-can
plc4j-transport-pcap-replay -> plc4j-transports-pcap-replay
plc4j-transport-raw-socket -> plc4j-transports-raw-socket
plc4j-transport-serial -> plc4j-transports-serial
plc4j-transport-tcp -> plc4j-transports-tcp
plc4j-transport-test -> plc4j-transports-test
plc4j-transport-udp -> plc4j-transports-udp
plc4j-transport-socketcan -> plc4j-transports-can-socketcan
plc4j-transport-virtualcan -> plc4j-transports-can-virtualcan
- The scraper was replaced by the new event-pump component:
plc4j-scraper -> plc4j-tools-event-pump
- Removed modules (no direct replacement):
plc4j-scraper-ng (experimental, dropped)
plc4j-transport-pcap-shared (obsolete)
Note: this release also adds a number of new SPI3 modules (e.g.
the "plc4j-spi-*" buffers/config/drivers/values split, the new
"plc4j-transports-api"/"-cotp"/"-tls" transports, the
"plc4j-utils-audit-log*" and "plc4j-utils-subscription-emulation"
utilities). These are new artifacts, not renames.
Bug Fixes
---------
- Fixed the Java S7 driver's tags reporting no address at all:
"getAddressString()" returned null, so anything carrying a tag as a string -
a log line, a browse result, a serialized request - got nothing from an S7
tag. It now spells the address the way the parser reads it back, including
the declared length of a fixed-length string and the counter number of a
COUNTER address, which is stored split across the byte and bit offsets.
- The Open Protocol driver's tag class now reports that it has no tag
addressing yet instead of returning null. "OpenProtocolTag.of()" handed a
null tag to callers of "prepareTag()", so the failure surfaced later as a
NullPointerException; it now throws PlcInvalidTagException, matching the
driver's tag handler, which already rejected every address.
- Fixed the Go S7 driver reading a fixed-length string from the wrong data
block. The long-form address ("%DB69.DBX68:STRING(10)") built its tag with
a hard-coded block number of zero, so it read DB0 and reported the result
as though it had come from DB69. The short form ("%DB69:68:STRING(10)")
and every non-string address were unaffected, as is PLC4J, which parses
the block number for all of them.
- Fixed serialization in the 'plc4x' proxy driver's message
codec, which did not configure the buffer integer/string
encodings under SPI3 and failed to serialize any message.
- OPC-UA: Fixed several issues: a hang during discovery /
encrypted-policy negotiation, a divide-by-zero when the server
provided no certificate, and hardened parsing of corrupt or
malicious responses that could previously cause an
OutOfMemoryError.
- ADS: The connect handshake now honors the configured AMS ports
instead of hardcoding RUNTIME_SYSTEM_01/851.
- ADS: Fixed an invalid size calculation in AdsDataTypeArrayInfo.
- ADS: Fixed serialization of STRING/WSTRING values.
- S7: Fixed duplicate TSAP information at the COTP and S7 level.
- Connection-cache: Fixed a deadlock when obtaining a connection,
plus a race condition and idle-timer/cleanup issues.
- The base driver now redacts password information from console
logs.
- TCP transport: Close the SocketChannel on a failed bind /
socket-option setup.
- Request objects were made more null-safe (#2280).
- OPC-UA: All consumers registered on a shared subscription now
receive notifications (previously only one consumer was
registered).
- AB-ETH: Fixed reading a tag after the SPI3 refactoring.
- The socket is now disconnected when the handshake fails instead
of being left hanging (#2290).
- Go Modbus: Hardened frame parsing against truncated/extended
frames, trailing CRCs from misbehaving gateways and TCP
keep-alive padding, with stream resynchronization on desync.
- Go: Numerous stability and resource-leak fixes (connection cache
no longer hands out dead connections, goroutine and codec-worker
leaks on Modbus reconnect, robust Modbus receive with
desync/resync, TCP/UDP transport deadline and reset races, codec
disconnect deadlock).
- Go: Generated code ignored the string encoding declared in the
mspec and read and wrote every string as UTF-8. A WSTRING, which
ADS declares as UTF-16LE, stopped at the first NUL byte, so
"wolf" decoded as "w". The generated code now carries the real
encodings (UTF-16LE, UTF-16BE, ASCII, ISO-8859-1, Windows-1252),
and the byte-based write buffer passes the single-byte encodings
through instead of zero-filling anything it did not recognise.
Reading a string likewise honors the requested encoding, decoding
UTF-16 per code unit rather than per byte.
- Java: The XML buffer could not read back everything its own
writer produced. A field discriminating on a typed enum was
written under the enum's name but read under the field's, and a
text node larger than the parser's chunk size - the ADS symbol
and data-type tables run to 70-80k characters - failed with
"Expected end element" because the reader was not coalescing.
- ADS (Go): Several fixes. The connection accepts the same
kebab-case parameters as the Java driver (source-ams-net-id and
friends), and a missing targetAmsNetId is now reported as such
rather than as sourceAmsNetId. The device-info request uses the
configured AMS ports instead of a hardcoded 851/800. Multi-tag
subscriptions run in request order rather than in map order,
honor the requested interval as the notification cycle time, and
no longer panic when a data type is missing from the table. The
data type table is keyed by the name symbols actually reference.
- ADS (Go): Direct-address reads and writes all failed with
"invalid tag item type" - the request paths expected a pointer
where the tag handler produced a value - and never filled in the
tag's data type. Hex address parts with an odd number of digits
(0x8) were rejected, though the Java driver accepts them.
- EtherNet/IP: A reply carrying fewer bytes than a single-element
tag declares no longer throws out of the response handler; it is
reported as INTERNAL_ERROR for that tag, which is what a
truncated multi-element reply already did.
- Modbus (Java): Several fixes to the read optimizer. The unit-id was
dropped while a request was being optimized, so an optimized read
went out with the default unit-id instead of the one the tag named
(#2686); a read of coils returned only the first value (#2060); a
non-BOOL tag pointed at coils is reported instead of being misread;
and the trailing register pad was sized from a string length of one,
so an odd number of STRING or WSTRING values picked up a pad byte
the registers did not need.
- EtherNet/IP: Fixed reading arrays, which generally did not work
(#1008). A response is no longer handed to a request it does not
belong to, and a tag address that cannot be parsed is reported for
that tag rather than failing the request.
- Tag addresses that cannot be parsed are reported per tag in the
EtherNet/IP, Modbus, OPC UA, S7 and simulated drivers, as the API
promises. One unparseable address used to fail the whole request,
including the tags that were fine.
- S7: A controller refusing a request because PUT/GET communication is
disabled is reported as ACCESS_DENIED. Only two of the three header
forms were mapped, so the 0x83/0x04 an S7-300 sends read as
INTERNAL_ERROR and a refused write could not be told from a failed
one (GH-599). Also fixed an asymmetry between the tags a request
accepts and the tags it hands back (#2388), and a timestamp for the
year 2000, which parsed but could not be serialized again.
- SPI: A request waiting for a permit no longer blocks the thread it
was submitted on. A driver chaining requests runs the follow-up on
whichever thread completed the previous one, typically the
connection's receive thread, which then parked waiting for a permit
that only it could have freed - the connection recovered when an
unrelated request timed out. Negotiated max-amq is often 1 to 3 on
an S7-300, which makes this easy to reach. Requests are queued and
started in submission order as permits free up.
- SPI: An unchecked exception out of a generated parser is reported as
the parse failure it is instead of ending the thread that owns the
channel, which left the transport claiming to be open with nobody
reading it.
- A message that stops short is refused rather than half-believed, in
both the Java and the Go bindings: a manual array whose reader
cannot make progress ends instead of looping (a firmata sysex
message ending mid-string was an endless supply of items), an
optional field the message announces has to actually be present, a
BACnet tag header length is derived only from fields that were read,
an S7 payload is read only when its parameter was, and the cast of a
parsed complex field is checked. plc4c and plc4py carry the same
nesting-depth bound as Java and Go.
- OPC-UA: The self-signed certificate the driver generates when no key
store is configured - the one most servers see on a first connect -
is now one a modern server accepts: signed with SHA-256 rather than
SHA1withRSA, a positive serial of sixteen octets rather than forty
random bytes, marked as an end entity rather than as a certificate
authority, and carrying a subject key identifier.
- OPC-UA: The client identity is the first key store entry that holds
a private key, not whatever alias comes first. The tutorial's own
set-up has "ca" sorting before "client", so the driver could hand
the server the CA entry, or carry on with a null private key; the
server just closed the connection. A store with no private key, an
entry that cannot be read (which is what a wrong key-store password
looks like) and a non-RSA key each now fail naming the entry and the
reason.
- OPC-UA: The certificate chain is sent in the asymmetric security
header, not the client certificate alone, so a server that trusts
the issuing CA rather than the certificate itself can build a path
to its trust anchor. The thumbprint still covers the client
certificate, as the specification requires.
- OPC-UA: A username token is encrypted with the algorithm the
server's user token policy names, instead of always RSA-OAEP, so
username and password authentication works against a server asking
for RSA-PKCS1.5 (#2154).
- OPC-UA: A subscription hands out one PlcSubscriptionHandle rather
than an identical one per tag (#1896) - corrected for every driver
that supports subscriptions - and tags registered with
addCyclicField(...) produce events again, through the same
cyclic-subscription emulation the other drivers use, since OPC UA
itself has no cyclic subscriptions (#1102).
- OPC-UA: The stream a key store is read from is closed, as is the
reader a BACnet EDE file is parsed from.
- ADS: With "load-symbol-and-data-type-tables=false" a symbolic
address is refused with a message saying the tables were not loaded,
rather than failing as an unknown symbol or, when browsing, as
nothing at all. Reading and writing are limited to direct addresses
in that mode; subscriptions are unaffected, since they resolve
symbol handles on the device (#1626).
- OPM: The PlcEntityManager caches the proxy classes it generates
rather than generating a new one on every connect, which used to
grow the metaspace of an application connecting repeatedly (#1935),
and List and array fields are mapped instead of mishandled (#1947).
- NiFi: The connection-string validator closes the connection it opens
to test the string. NiFi validates on every configuration change and
while the dialog is open, so each round left a connection behind -
for OPC UA a secure channel and a session the device holds until
they time out, which on hardware with a small connection limit locks
everyone else out. It also catches an unchecked rejection, which is
how some drivers report a bad connection string.
- KNXnet/IP: A browse answers the query it was asked rather than every
query in the request, and reading a knxproj cleans up what it
unpacked.
- Go: A future publishes its error before the flag that releases
whoever is waiting on it, a blocking subscription consumer no longer
wedges the poller for everyone else, the codec survives a reconnect
and a late expectation, and the KNXnet/IP discoverer no longer leaks
a goroutine and a socket per discovery.
==============================================================
Apache PLC4X 0.13.1
==============================================================