Search talk: x-point γƒ‘γ‚Ήγƒ―γƒΌγƒ‰εΏ˜γ‚ŒγŸ

 
<< < 1 .. 8 9 10 (Page 10 of 10)

Post by ara32 on CODESYS 4 Linux: CODESYS Forge talk (Post)
Hello! I managed to correctly launch CODESYS Developer Studio 3.5.17, almost all functionality works. The only issue remaining is that when connecting to a device and obtaining its public key, the NCryptEncrypt function is called, which is not fully implemented in the DLL source code, resulting in the connection not being established. Currently, the code of this function in the Wine repository looks like this: SECURITY_STATUS WINAPI NCryptEncrypt(NCRYPT_KEY_HANDLE key, BYTE *input, DWORD insize, void *padding, BYTE *output, DWORD outsize, DWORD *result, DWORD flags) { struct object *key_object = (struct object *)key; TRACE("(%#Ix, %p, %lu, %p, %p, %lu, %p, %#lx)\n", key, input, insize, padding, output, outsize, result, flags); if (flags & ~(NCRYPT_NO_PADDING_FLAG | NCRYPT_PAD_OAEP_FLAG | NCRYPT_PAD_PKCS1_FLAG | NCRYPT_SILENT_FLAG)) { FIXME("Flags %lx not supported\n", flags); return NTE_BAD_FLAGS; } if (flags & NCRYPT_NO_PADDING_FLAG || flags & NCRYPT_PAD_OAEP_FLAG) { FIXME("No padding and oaep padding not supported\n"); return NTE_NOT_SUPPORTED; } if (key_object->type != KEY) return NTE_INVALID_HANDLE; return map_ntstatus(BCryptEncrypt(key_object->key.bcrypt_key, input, insize, padding, NULL, 0, output, outsize, result, flags)); } The program crashes due to the NCRYPT_PAD_OAEP_FLAG flag. I'm not proficient in C++, but I attempted to add handling myself, and here's the result: SECURITY_STATUS WINAPI NCryptEncrypt(NCRYPT_KEY_HANDLE key, BYTE *input, DWORD insize, void *padding, BYTE *output, DWORD outsize, DWORD *result, DWORD flags) { struct object *key_object = (struct object *)key; TRACE("(%#Ix, %p, %lu, %p, %p, %lu, %p, %#lx)\n", key, input, insize, padding, output, outsize, result, flags); if (flags & ~(NCRYPT_NO_PADDING_FLAG | NCRYPT_PAD_OAEP_FLAG | NCRYPT_PAD_PKCS1_FLAG | NCRYPT_SILENT_FLAG)) { FIXME("Flags %lx not supported\n", flags); return NTE_BAD_FLAGS; } if (flags & NCRYPT_NO_PADDING_FLAG) { FIXME("No padding not supported\n"); return NTE_NOT_SUPPORTED; } BCRYPT_OAEP_PADDING_INFO oaepInfo = { 0 }; oaepInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM; NTSTATUS status = BCryptEncrypt(key_object->key.bcrypt_key, input, insize, &oaepInfo, NULL, 0, output, outsize, result, flags); if (key_object->type != KEY) return NTE_INVALID_HANDLE; return map_ntstatus(BCryptEncrypt(key_object->key.bcrypt_key, input, insize, padding, NULL, 0, output, outsize, result, flags)); } Now, when calling the connection, it crashes with the error "bcrypt:BCryptEncrypt flags 0x4 not implemented." Can anyone help with enhancing this functionality or at least point me in the right direction?
Last updated: 2024-03-22

Post by baltzer on ICertificateVerifier.VerifyCertificate doesn't appear to override an ERR_CERT_HAS_EXPIRED rejection in TCP_Client.Upgrade() CODESYS Forge talk (Post)
Follow-up: working workaround found (routing TLS outside CODESYS) For anyone finding this thread later with the same problem (a third-party device presenting a self-signed and/or expired certificate that ICertificateVerifier/udiVerificationMode won't let you connect through) - we never got a working fix within NBS itself, but found a practical workaround worth sharing. Approach: run a local TLS-terminating proxy on the same host as the CODESYS runtime, and have CODESYS talk plain, unencrypted TCP to it instead of connecting to the remote device directly over TLS: CODESYS --plain TCP--> 127.0.0.1:<port> --stunnel/TLS--> <remote device>:<port> We used stunnel (client mode), with certificate verification explicitly disabled at that layer (verifyChain = no, verifyPeer = no) - a simple, well-documented one-line option, unlike anything we could get working inside NBS. Running it as a systemd service with Restart=always means it survives reboots and recovers automatically if the proxy or the remote device drops. On the CODESYS side, this required no changes beyond pointing TCP_Client at 127.0.0.1 instead of the device's real IP, and removing the TLSContext/ICertificateVerifier code entirely - the rest of our application-layer logic (framing, checksums, etc.) was completely unaffected, since none of that ever depended on where the TLS termination happened. Confirmed working end-to-end against a real device with an expired self-signed certificate, running on CODESYS Control for Raspberry Pi SL (Linux). We haven't yet set this up on Windows - stunnel does have Windows builds available, so the same general approach should be possible there too, but that combination isn't tested or confirmed by us at this point. Caveat: this only makes sense when the proxy and the CODESYS runtime are on the same trusted host (in our case, both on the same Raspberry Pi) - don't expose the proxy's plaintext side on a reachable network interface, since that would create an unauthenticated plaintext path to the device. The original question - whether there's a supported way to override an ERR_CERT_HAS_EXPIRED/self-signed rejection from within NBS's own TLSContext/ICertificateVerifier - remains open as far as we know. Still happy to hear from anyone who's solved that particular piece, since the workaround above is a practical detour rather than an actual answer to the original question.
Last updated: 2026-08-06

Post by alexschooneveld on OPC UA PubSub SL 1.3 β€” UADP WriterGroup with assigned PSS.SecurityGroup still publishes plaintext CODESYS Forge talk (Post)
I am currently investigating an OPC UA PubSub connection over UDP. When I don't use encryption, the publish and subscribe are working correctly. But with encryption it does not. Environment OPC UA PubSub SL 1.3.0.0 (namespace UADP) OPC UA PubSub Security 1.3.0.0 (namespace PSS) OPC UA PubSub Base 1.3.0.0 (namespace PSB, incl. PSS.SecurityGroup / PSS.CONFIG) Programmatic PubSub in a CFC: UADP.Configuration β†’ UADP.Connection β†’ UADP.writerGroup β†’ UADP.writer β†’ writerDataSet, plus a CyclicCall gated by xEnable. Goal: publish secured UADP, SignAndEncrypt, policy PubSub-Aes256-CTR. What I do (one-shot, before xEnable := TRUE): fbSecurityGroup.SetInitialValue( 'http://opcfoundation.org/UA/SecurityPolicy#PubSub-Aes256-CTR', PSB.SECURITY.SIGNING_AND_ENCRYPTION); stSecurityCfg := fbSecurityGroup.GetConfig(eErrorID => eError); // eError=NO_ERROR, udiEncryptionKeySize=32 eError := fbSecurityGroup.SetSecurityKeys(udiTokenId, ADR(abyKey), SIZEOF(abyKey), 2436001000); // eError=NO_ERROR, SIZEOF=68 // writerGroup.itfSecurityGroup := fbSecurityGroup -- set in the WriterGroup block's Parameters (a per-scan code write got overwritten) What I verified eError = NO_ERROR after both GetConfig and SetSecurityKeys; udiEncryptionKeySize = 32. Key length = 68 bytes (signing 32 β€– encrypt 32 β€– nonce 4). itfSecurityGroup is set via the WriterGroup's Parameters (so it isn't clobbered each scan). Init runs before xEnable (the writer doesn't run with xEnable=FALSE). The UADP.writerGroup FB exposes only itfSecurityGroup for security β€” no SecurityMode/MessageSecurityMode property. Result: the published datagrams are still plaintext β€” ExtendedFlags1 = 0x01 (security bit 0x10 clear), no security header: b1 01 29 00 0f 16 00 … (PublisherId 41, WriterGroupId 22, RawData, no security) Questions With UADP.writerGroup, is assigning a configured + keyed PSS.SecurityGroup to itfSecurityGroup sufficient to enable message security, or is there an additional step/property/method to switch the WriterGroup to SignAndEncrypt? At what point in the WriterGroup lifecycle is itfSecurityGroup read? Must it be assigned/keyed before xActive, and does the group need a stopβ†’start to pick it up? Is there a required call order, and does SetSecurityKeys need to be called once or repeatedly? Should security be configured on the Connection/Configuration level rather than (or in addition to) the WriterGroup? Is there a working example of secured (SignAndEncrypt) programmatic UADP publishing with this library, or a known limitation in 1.3? How can I read back at runtime whether security is actually active (via itfDiagnostics or similar)? Additional information I can confirm that the consumer side works β€” i.e. a standard subscriber decrypts the same keys fine β€” so the keys/profile aren't the issue. The Wireshark capture of the published message is: 0000 b1 01 29 00 0f 16 00 df 0d bb 25 01 00 0e 00 1b ..).......%..... 0010 0e 00 00 00 00 00 00 00 ........
Last updated: 2026-06-22

Post by alexschooneveld on OPC UA PubSub SL 1.3 β€” UADP WriterGroup with assigned PSS.SecurityGroup still publishes plaintext CODESYS Forge talk (Post)
I am currently investigating an OPC UA PubSub connection over UDP. When I don't use encryption, the publish and subscribe are working correctly. But with encryption it does not. Environment OPC UA PubSub SL 1.3.0.0 (namespace UADP) OPC UA PubSub Security 1.3.0.0 (namespace PSS) OPC UA PubSub Base 1.3.0.0 (namespace PSB, incl. PSS.SecurityGroup / PSS.CONFIG) Programmatic PubSub in a CFC: UADP.Configuration β†’ UADP.Connection β†’ UADP.writerGroup β†’ UADP.writer β†’ writerDataSet, plus a CyclicCall gated by xEnable. Goal: publish secured UADP, SignAndEncrypt, policy PubSub-Aes256-CTR. What I do (one-shot, before xEnable := TRUE): fbSecurityGroup.SetInitialValue( 'http://opcfoundation.org/UA/SecurityPolicy#PubSub-Aes256-CTR', PSB.SECURITY.SIGNING_AND_ENCRYPTION); stSecurityCfg := fbSecurityGroup.GetConfig(eErrorID => eError); // eError=NO_ERROR, udiEncryptionKeySize=32 eError := fbSecurityGroup.SetSecurityKeys(udiTokenId, ADR(abyKey), SIZEOF(abyKey), 2436001000); // eError=NO_ERROR, SIZEOF=68 // writerGroup.itfSecurityGroup := fbSecurityGroup -- set in the WriterGroup block's Parameters (a per-scan code write got overwritten) What I verified eError = NO_ERROR after both GetConfig and SetSecurityKeys; udiEncryptionKeySize = 32. Key length = 68 bytes (signing 32 β€– encrypt 32 β€– nonce 4). itfSecurityGroup is set via the WriterGroup's Parameters (so it isn't clobbered each scan). Init runs before xEnable (the writer doesn't run with xEnable=FALSE). The UADP.writerGroup FB exposes only itfSecurityGroup for security β€” no SecurityMode/MessageSecurityMode property. Result: the published datagrams are still plaintext β€” ExtendedFlags1 = 0x01 (security bit 0x10 clear), no security header: b1 01 29 00 0f 16 00 … (PublisherId 41, WriterGroupId 22, RawData, no security) Questions With UADP.writerGroup, is assigning a configured + keyed PSS.SecurityGroup to itfSecurityGroup sufficient to enable message security, or is there an additional step/property/method to switch the WriterGroup to SignAndEncrypt? At what point in the WriterGroup lifecycle is itfSecurityGroup read? Must it be assigned/keyed before xActive, and does the group need a stopβ†’start to pick it up? Is there a required call order, and does SetSecurityKeys need to be called once or repeatedly? Should security be configured on the Connection/Configuration level rather than (or in addition to) the WriterGroup? Is there a working example of secured (SignAndEncrypt) programmatic UADP publishing with this library, or a known limitation in 1.3? How can I read back at runtime whether security is actually active (via itfDiagnostics or similar)? Additional information I can confirm that the consumer side works β€” i.e. a standard subscriber decrypts the same keys fine β€” so the keys/profile aren't the issue. The Wireshark capture of the published message is: 0000 b1 01 29 00 0f 16 00 df 0d bb 25 01 00 0e 00 1b ..).......%..... 0010 0e 00 00 00 00 00 00 00 ........
Last updated: 2026-06-22

Post by dwpessoa on CNC Jumps G20 - SMC_NCInterpreter and long time to process CODESYS Forge talk (Post)
I am studying and developing a Softmotion+CNC system for a machine that executes multiple pieces. The G code program is written by the machine operator and each cycle execute 1 piece. The programs are large, exceeding 1000 lines and using up to 8 axes (X, Y, Z, A, B, C, P and Q). The machine needs to run cyclically, executing N pieces (selected by the Operator)... so I tested it using Looping and counters (G36 G37 and G20) and it worked, but it takes a long time to process, and the more pieces I need, the longer the processing time and this is totally impracticable. I found this solution which was very good, and for a few cycles it works well, but for 99999 pieces of a program with 1000 lines, it doesn't work very well... Another solution I tested is to maintain the interpolator with an automatic restart, that is, I load the program without looping (without G20) and give it another start as soon as it finishes. This partially resolved it, but there is still a delay in processing SMC_NCInterpreter in each restart :(. Another solution I thought of is to manually create the SMC_GEOINFO structure and then reuse it, avoinding the Interpreter, but reading the documentation and checking the structure filled by standard blocks, I noticed that there doesn't seem to be a "JUMP" function in the structure! In other words, the SMC_NCInterpreter actually keeps copying and copying the program section for each jump (G20)... If I repeat a 10-line program 1000 times, I will have a structure with more than 10000 lines... possibly this is the cause of take so long to process. Has anyone ever had a problem like this? I believe the same thing happens with typical applications with manipulator robots using Codesys in continuous cycles, and I would like to know if there is any solution, or even if I am misinterpreting the G20 question in SMC_GEOINFO. Thanks!
Last updated: 2023-09-20

Post by bbm1995 on Camera RTSP Feed CODESYS Forge talk (Post)
Hi dgrard, I had the same issue for a long time, but on a WAGO webvisu. Now I'm glad that I can share my solution. I don't know if you are trying to use the webvisu or the target visu, but this works on webvisu: Get "go2rtc" and set it up according to the documentation. I'm running the Windows binary. Here's my example of the go2rtc.yaml config file (contains working example streams): api: listen: ":1984" # default ":1984", HTTP API port ("" - disabled) origin: "*" # default "", allow CORS requests (only * supported) static_dir: "www" # default "", folder for static files (custom web interface) tls_listen: ":443" # default "", enable HTTPS server tls_cert: "./SSL/fullchain.pem" tls_key: "./SSL/privatekey.pem" streams: # Streams with multiple links will fall back on the next link. ABUS TVIP48511: - rtsp://<username>:<password>@<hostname>:<port>/ch1/main - rtsp://<username>:<password>@<hostname>:<port>/ch1/sub # Diagnostic connections Kirchhoff Institute for Physics - Germany: http://pendelcam.kip.uni-heidelberg.de/mjpg/video.mjpg Blanton Bottling, Kentucky - USA: http://camera.buffalotrace.com/mjpg/video.mjpg Tokyo - Japan: http://61.211.241.239/nphMotionJpeg?Resolution=320x240&Quality=Standard Tampere Hacklab - Finland: http://tamperehacklab.tunk.org:38001/nphMotionJpeg?Resolution=640x480&Quality=Clarity Soltorget Pajala - Sweden: http://195.196.36.242/mjpg/video.mjpg Kaiskuru Skistadion - Norway: http://77.222.181.11:8080/mjpg/video.mjpg webrtc: listen: ":8555" #ice_servers: # - urls: [ "stun:localhost:3478" ] # username: "" # credential: "" Access the webinterface of go2rtc and get the link of your stream. Use the link as for your browser frame in the visualization. Depending on your device, you need to be able to access the file /etc/lighttpd/lighttpd.conf or /etc/lighttpd/webvisu.conf and change the contents similar to this one, otherwise you'll get CORS and CSP errors in the browser and you won't be able to view the stream on the webvisu: # Webvisu specific settings $SERVER["socket"] =~ port_webvisu_used_any { url.redirect += ( "^/webvisu/?$" => "/webvisu/webvisu.htm" ) $HTTP["url"] =~ "^/webvisu/?" { var.response_header_policy_webvisu = ( # CSP for WebVisu, allowing inline sources. "Content-Security-Policy" => "default-src 'self' 'unsafe-inline'; media-src *; frame-src *", # CORS for WebVisu, allowing any origin to access. "Access-Control-Allow-Origin" => "*", # Tell older browsers that this page can only be displayed if all ancestor # frames are same origin to the page itself. "X-Frame-Options" => "SAMEORIGIN" ) # Response header policy for WebVisu setenv.set-response-header = var.response_header_policy_webvisu setenv.set-response-header += var.response_header_policy_common }
Last updated: 2023-11-14

Post by paulg on RasPi CAA Serial example - unexpected behavior during debug CODESYS Forge talk (Post)
I've trimmed down the CAA Serial Codesys example to only listen on one port but, when stepping through the Case structure in debug mode, it jumps out of the structure during a specific point in every scan (I'll point it out below after describing the setup and listing the code). I'm using a Pi 4 Model B, and I have an Arduino Nano Every plugged in via USB which is streaming the following serial message at 1 Hz: Time since opening connection: 1 s Time since opening connection: 2 s ...and so on. The Pi shows the Nano at /dev/ttyACM0 so I edited CODESYSControl_User.cfg to read: Linux.Devicefile=/dev/ttyACM The code in my PLC_PRG is (ignore some of the comments, I hadn't deleted them out from the original example): PROGRAM PLC_PRG VAR xStartTest : BOOL:= TRUE; iState : INT; xTestDone : BOOL;(* True, when the test was done succesfully *) (* Settings to communicate with the COM Port *) aCom1Params : ARRAY [1..7] OF COM.PARAMETER; como1 : COM.Open; comc1 : COM.Close; comw1 : COM.Write; comr1 : COM.Read; //sWrite : STRING := 'Test String!'; sRead : STRING(25); szRead : CAA.SIZE; xCom1OpenError : BOOL; xCom1CloseError : BOOL; xCom1WriteError : BOOL; xCom1ReadError : BOOL; END_VAR //This example shows the communication of two COM Ports with each other. //The first one writes a string of characters, which is read by the second one. //After successful execution, the two COM Ports are closed and the test is done. IF xStartTest THEN CASE iState OF 0: //The parameters are set for the COM Port aCom1Params[1].udiParameterId := COM.CAA_Parameter_Constants.udiPort; aCom1Params[1].udiValue := 1; // the correct Port should be adapted aCom1Params[2].udiParameterId := COM.CAA_Parameter_Constants.udiBaudrate; aCom1Params[2].udiValue := 115200; aCom1Params[3].udiParameterId := COM.CAA_Parameter_Constants.udiParity; aCom1Params[3].udiValue := INT_TO_UDINT(COM.PARITY.NONE); aCom1Params[4].udiParameterId := COM.CAA_Parameter_Constants.udiStopBits; aCom1Params[4].udiValue := INT_TO_UDINT(COM.STOPBIT.ONESTOPBIT); aCom1Params[5].udiParameterId := COM.CAA_Parameter_Constants.udiTimeout; aCom1Params[5].udiValue := 0; aCom1Params[6].udiParameterId := COM.CAA_Parameter_Constants.udiByteSize; aCom1Params[6].udiValue := 8; aCom1Params[7].udiParameterId := COM.CAA_Parameter_Constants.udiBinary; aCom1Params[7].udiValue := 0; //The first Port is opened with the given parameters como1(xExecute := TRUE, usiListLength:=SIZEOF(aCom1Params)/SIZEOF(COM.PARAMETER),pParameterList:= ADR(aCom1Params)); IF como1.xError THEN xCom1OpenError := TRUE; iState := 1000; END_IF //After a successful opening, the next state is reached IF como1.xDone THEN iState := 15; END_IF 15: // the reading process is started comr1(xExecute := TRUE,hCom:= como1.hCom, pBuffer:= ADR(sRead), szBuffer:= SIZEOF(sRead)); IF comr1.xError THEN xCom1ReadError := TRUE; END_IF //After completion the size of the written bytes are saved IF comr1.xDone OR comr1.xError THEN szRead := comr1.szSize; iState := 20; END_IF 20: // If everything was successful the ports are closed and the handles are released comc1(xExecute := TRUE,hCom:= como1.hCom); IF comc1.xError THEN xCom1CloseError := TRUE; END_IF IF comc1.xDone OR comc1.xError THEN iState := 25; END_IF 25: // The first port is closed and the used handle released xTestDone := TRUE; xStartTest := FALSE; iState := 0; como1(xExecute := FALSE); comw1(xExecute := FALSE); comc1(xExecute := FALSE); ELSE iState := 0; END_CASE END_IF I realize as I write this that the .udiPort should be 0 and not 1, but that shouldn't be causing the issue I'm seeing. I'm forcing xStartTest:=TRUE every scan so that I can step into each line and observe what's happening. What I see is that the port parameters are set and the port is opened with no errors, but the code jumps out of the case structure to the last line every time it reaches (and I step into) the iState:=15 line (at the end of the iState:=0 block). So every scan cycle it goes through the block for iState=0 and jumps out at the same spot. I'm a little new to PLC programming so I may be misunderstanding the flow, but shouldn't this case structure keep moving down in the same scan? If it only handles one case per scan, why doesn't the value of iState persist? Thanks! Update: I restarted the Codesys control today and I was then able to see an error for como1.eError of "WRONG_PARAMETER". I tried doing some digging and another post made me think I should add another line to CODESYSControl_User.cfg, so I now have: [SysCom] Linux.Devicefile=/dev/ttyACM portnum := COM.SysCom.SYS_COMPORT1 So now when I set .udiPort to 1, I get "NO_ERROR" but I also don't read anything from the port (i.e. szRead = 0 always). If I try setting the port to 0 (which I'm confused about, because I added a COMPORT1 line but the device shows on the Pi as ACM0), I get the "WRONG_PARAMETER" error again. Is there an easier way to troubleshoot the Pi and view what ports the Codesys runtime is actually able to see while the Pi is running?
Last updated: 2024-06-06

Post by mubeta on Some 'pathetic' errors in SoftMotion program CODESYS Forge talk (Post)
Hello everyone, I have a very simple program for the process, but it's driving me crazy and I can't see the problems I'm left with: Short topological description: Dual Core Berghof controller with softmotion runtime version 3.5.19.30; Two axes with servodrive on canopen bus, clocked distributed from master; Ethercat I/O node; 2 ms ethercat task, 2 ms canopen bus cycle time; I/O objects of the canopen master and canopen drives connected to the ethercat task cycle; Problem 1: Two separate programs each manage their own axis and drive, with separate state machines. A first axis moves primarily in velocity, except having to position itself absolutely at a predetermined point at the end of the job; the second axis, on the other hand, is a paper unwinder that changes, for each job cycle, from actions in absolute, relative, and cam displacement with the master axis. Well, the state machine of both axes was written in such a way as to call running the useful FB and change it on state change in this way: CASE i_stateMachine OF 0: o_Power(Enable := TRUE, bRegulatorOn := FALSE, bDriveStart := FALSE, Axis := o_PaperUnwinderAxis); o_MoveAbs(Execute := FALSE, Axis := o_PaperUnwinderAxis); o_MoveRel(Execute := FALSE, Axis := o_PaperUnwinderAxis); o_CamSelect(Execute := FALSE, Master := o_MachineAxis, Slave := o_PaperUnwinderAxis, CamTable := cam_PaperUnwinder); o_CamIn(Execute := FALSE, Master := MachineEncoder, Slave := o_PaperUnwinderAxis); o_CamOut(Execute := FALSE, Slave := o_PaperUnwinderAxis); o_SetPosition(Execute := FALSE, Axis := o_PaperUnwinderAxis); IF ... THEN i_StateMachine := 10; END_IF; 10: o_Power( Enable := TRUE, bRegulatorOn := TRUE, bDriveStart := TRUE, Axis := o_PaperUnwinderAxis ); IF o_Power.Status THEN i_StateMachine := 20; END_IF; 20: (* Avanzamento carta *) o_MoveAbs( Execute := TRUE, Position := o_Somewhere, Velocity := 25.0, Acceleration := 3666.7, Deceleration := 3666.7, Jerk := 48000.0, Direction := MC_DIRECTION.positive, Axis := o_PaperUnwinderAxis ); IF o_MoveAbs.Done THEN o_MoveAbs(Execute := FALSE, Axis := o_PaperUnwinderAxis); i_StateMachine := 30; END_IF 30: d_HomingPosition := ...; o_SetPosition( Execute := TRUE, Position := d_HomingPosition, Mode := FALSE, Axis := o_PaperUnwinderAxis ); (* ... *) IF o_SetPosition.Done = TRUE THEN o_SetPosition(Execute := FALSE, Axis := o_PaperUnwinderAxis ); o_LogServer.Append(sMessage := '...', lscClass := LOGSERVER_CLASS.ALWAYS, sdt := o_CommonsMgrData.systime.sdtLocal); i_StateMachine := 40; END_IF; 50: ... The code above is a sketchy example of what I wanted to write. But it gives me a spot problem: in some, the state change results in a drive error, which is unrecoverable except with a reinitialization via SM3_ReinitDrive(). Things are improved a little if in the program I always run the call of all softmotion blocks in this way: o_Power(Axis := o_PaperUnwinderAxis); o_Jog(Axis := o_PaperUnwinderAxis); o_Halt(Axis := o_PaperUnwinderAxis); o_MoveAbs(Axis := o_PaperUnwinderAxis); o_MoveRel(Axis := o_PaperUnwinderAxis); o_CamIn(Master := MachineEncoder, Slave := o_PaperUnwinderAxis); o_CamOut(Slave := o_PaperUnwinderAxis); If I don't execute all the calls of all the motion FBs used, when exchanging machine state often (but not always), the axis goes into error with event id THE_FB_WASNT_CALL... Done a little diagnostics it seems that the FBs return the bDone, before they are completely terminated. I tried doing the machine state exchange not with the bDone bit of the FBs, but with the 'standstill' state of the axis. It didn't seem to change anything. Problem 2: During the use SM3_ReinitDrive() I get the erro in the log: "NetID 0: SDO read error for object 16#607C..." Assuming that the device involved it's one of the two servodrive, (no others device are present in the network), I don't found any object 0x607C in the 'possible object list in/out' of the two drive, and I don't understand where this object can be listed. So any ideas and suggestions regarding these two issues will be very, very welcome. If you need the source project, I am willing to send it.
Last updated: 2024-07-17

Post by smeitink on Timeout Error in Modbus Communication with WAGO PFC200 and iEM2050 Meter using 750-652 Module CODESYS Forge talk (Post)
Hi all, I'm looking for help with an issue I've come across while trying to facilitate Modbus communication between a WAGO PFC200 PLC using a 750-652 communication module and an iEM2050 Series Single Phase Energy Meter. I believe to have everything wired and setup correcty, but I keep running into a "Error time out" message, and by now I don't really know what else to try. My setup is as follows: A PFC200 Wago PLC, which has 2 750-652 Serial Interfaces extension modules connected to its field bus. I'm using one of these to talk to a Schneider iEM2050 - kWh-meter over modbus. I have connected terminal 23 (A) of the iEM2050 to connector 6 (A) on the 750-652. I have connected terminal 24 (B) of the iEM2050 to connector 2 (B) of the 750-652. I'm using 200mm of twisted together wires to connected them both, and I have placed a 120 ohm resistor between A and B at both ends. I've attached relevant pinout images to this post. I then wrote a simple program that configures the Mobus port, as per the datasheet of the iEM2050. You can find an image of the relavent page attached to this post too. This is my program: PROGRAM PLC_PRG VAR Master: FbMbMasterSerial; xIsConnected: BOOL; xError: BOOL; iIndex: INT := 1; xTrigger: BOOL; utQuery : typMbQuery := ( bUnitId := 1, // The Modbus unit or slave address bFunctionCode := 4, // Function code for reading input registers uiReadAddress := 1829, // adress for the Power on off counter uiReadQuantity := 1 // Quantity of registers to read ); iStep: INT; oStatusModbus: WagoSysErrorBase.FbResult; utResponseModbus: typMbResponse; xConnect: BOOL := FALSE; delayTimer: TON; END_VAR Master( xConnect:= xConnect, I_Port:= _750_652_24_1, udiBaudrate:= 9600, usiDataBits:= 8, eParity:= WagoTypesCom.eTTYParity.Even, eStopBits:= WagoTypesCom.eTTYStopBits.One, eHandshake:= WagoTypesCom.eTTYHandshake.None, ePhysical:= WagoTypesCom.eTTYPhysicalLayer.RS485_HalfDuplex, xIsConnected=> xIsConnected, xError=> xError, oStatus=> oStatusModbus, eFrameType:= WagoAppPlcModbus.eMbFrameType.RTU, tTimeOut:= T#5S, utQuery:= utQuery, xTrigger:= xTrigger, utResponse:= utResponseModbus); delayTimer(IN := TRUE, PT := T#3S); // Use the Q output of the timer to set xConnect after the delay IF delayTimer.Q THEN xConnect := TRUE; END_IF CASE iStep OF 0: //Wacht totdat de master de poort geopend heeft IF xIsConnected THEN iStep := 1; END_IF 1: //Stuur request naar de slave xTrigger := TRUE; iStep := 2; 2: //Wacht totdat de master klaar is met het afhandelen van de request IF NOT xTrigger THEN iStep := 3; END_IF END_CASE The TON delay before opening the port is due to a an error I encountered when opening it straight away. This seems to be a bug, as described here. However, the TON solved that particular issue. I tried reading multiple registers, but like I said, I still always end up with the "Error time out". What else can I test or try at this point?
Last updated: 2024-02-24

Post by brouwyka on JSONByteArrayWriter string result incorrect order CODESYS Forge talk (Post)
Hi @TimvH, As discussed in our email contact, your example does not actually test/reproduce the bug I am describing: Adding to the JSON builder on later cycles works fine. Your example works because you do the following: 1. You finish the first cycle (xFirst) with adding an object to the array with 2 fields; 2. When the next cycle is triggered (xAdd), you immediately start with adding another object to that array. This works fine as the last thing you did before #2 was the addition of the array and an object to it. To reproduce the bug you should be doing the following instead, as I showed with the code I shared with my first post: 1. Create the array - save the index that is returned; 2. Add an item, either a primitive or an object, to the array; 3. Add an item outside the array (anything: a primitive, a new array, a nested object, etc); 4. Add another item to the array using the index you saved at #1. You will now see, as I shared in my initial post, that the second item is placed completely outside the root JSON object. This also happens to anything else you try to add after step #4: everything after this point will be added outside the root JSON object: the JSON is completely broken. Important to note is that this not only happens with arrays, but also with nested JSON objects. Once you add something outside of a nested JSON object, you can no longer add anything to that nested JSON object, as that causes the exact same bug. This also applies to arrays of objects, so if in your test you had tried adding a new key-value pair to the first nested object in your array after you created the second nested object, you would also run into this bug. It seems that the JSONByteArrayWriter (I haven't tested the other writers in the JSON Utilities SL library, so I don't know if they suffer from the same problem) simply does not handle any JSON fields that add brackets (so arrays with "[" & "]" and nested objects with "{" & "}") well, and closes them prematurely instead of checking if any later JSONElements in the JSONData's array belong to any of these bracketed fields. After reviewing the objects & functions of the JSON Utilities SL library, my guess is that either the JSONByteArrayWriter linearly goes through the array of JSONElements in the JSONData and only checks the diParentIndex of each JSONElement in direct ascending order, OR if it does use the JSONElement.GetChildren(); method, that this method is either broken and doesn't give all children correctly to the writer. Neither explains why everything completely breaks, and you cannot even add to the root JSON object anymore, however, so there is probably more than just that going wrong in the writer. To me, after a full week of testing and attempting workarounds, this seems like a bug in the library that needs to be fixed by Codesys, as I cannot see anything wrong in the JSONData constructed by the JSONBuilder - this seems purely a problem in the writer.
Last updated: 2026-06-15

Post by thejesh on WebVisu: switch-frame destroys and rebuilds referenced visualization - is retention configurable? CODESYS Forge talk (Post)
Hi, HTML5 Conrtrol elements on CODESYS V3.5 SP22 Patch 2, WebVisu only (no TargetVisu). The client is Chromium 147 in kiosk mode on a Raspberry Pi CM5 panel; the runtime is CODESYS Control Win V3 x64/CODESYS Control RTE V3 x64 on a separate IPC. I have measured that changing a Frame element's switch-frame variable destroys the currently referenced visualization and fully rebuilds it on return, including all embedded HTML5 controls. We would like to know whether this lifecycle is configurable. Measurement 1: Curently, our production page contains a good number of custom HTML5 controls and will only increase , each of which the WebVisu client instantiates as a separate about:srcdoc iframe. I counted live targets via the Chromium remote debugging endpoint (curl http://localhost:9222/json/list) before and after switching. Production displayed (index 0) 108 iframes Switched to a light page (index 1) 46 iframes Switched back to Production 108 iframes 62 controls are destroyed on leaving the page and re-instantiated on return. Observed latency was approximately 2.5 -5s away from the heavy page and approximately 4.0 -5 s back to it. This was measured with the panel otherwise idle: 2.8% system CPU, 97.1% idle, 2 h 56 min uptime, no remote-desktop session running, hardware acceleration confirmed active in chrome://gpu. I had previously measured the same behaviour under a much higher CPU load and have since resolved that load separately (VISU_TASK cycle period), so I am confident the rebuild is the frame lifecycle itself and not CPU starvation. Measurement 2 β€” frame offset instead of switch index I then displayed the same overlay page by a different mechanism. Instead of using a switch-frame index, I placed the page in a fixed Frame parked off-screen below the visible canvas, and drive its Y offset from an INT in Structured Text to move it into view. With this approach the overlay appears immediately, the production page underneath is never torn down and keeps updating, and no visualization is destroyed or rebuilt in either direction. The iframe count does not change at any point. This is the same visualization, containing the same HTML5 controls, as in Measurement 1 β€” the only difference is how it is brought on screen. So the rebuild appears to be a property of the switch-frame mechanism specifically, not of the page or its control count. Questions: Is the destroy-and-rebuild behavior on switch-frame the intended and documented lifecycle for WebVisu? Is there any setting, property or client option that causes a referenced visualization to remain instantiated when it is not the active index β€” the equivalent of keeping it mounted but not displayed? Does the runtime or client offer any form of caching or pre-instantiation for referenced visualizations, so that a return switch does not pay the full construction cost? Is the cost specific to HTML5 controls, or would the same rebuild occur with an equivalent number of native visualization elements? We ask because it would inform whether reducing our HTML5 control count is a viable solution. Is there a recommended pattern for showing a full-screen page over a live production screen without tearing the production screen down? My current workaround is to keep every page permanently instantiated and move it on and off screen by driving a frame offset variable, which avoids the rebuild entirely but means every page stays live and consumes client resources for the life of the session. This works, but I would prefer to use the supported mechanism if one exists. Any help on this is greatly appreciated. Thank you!
Last updated: 2026-08-07

Post by clesio on CAA.File.Write – Unexpected characters when writing a CSV file CODESYS Forge talk (Post)
Hello everyone, I am trying to create a routine in CODESYS to export a report to a CSV file using the CAA.File library. The file is created successfully, but I am getting some unexpected/unknown characters in the generated CSV. The problem seems to be related to the SIZEOF() function. I am using it for the szSize parameter of File.Write, as recommended in the CODESYS documentation. However, when I use SIZEOF(), the CSV contains extra characters and parts of previous strings. I also tried using LEN(), but in my application the file is created empty, even though the string and the calculated length appear to be correct. Here is the relevant part of my code: PROGRAM Relatorio VAR // Bool b_Executa_fb_Cria_Arquivo : BOOL; b_Executa_fb_Fecha_Arquivo : BOOL; b_Escreve_dados : BOOL; b_Erro_fb_Escreve : BOOL; //STR s_Nome_Arquivo : STRING; s_Palavra : STRING; s_Palavra_Anterior : STRING; s_Data_Atualizada_Formatada : STRING; s_Dia : STRING; s_Mes : STRING; s_Ano : STRING; s_Hora_Atualizada_Formatada : STRING; s_Hora : STRING; s_Minu : STRING; s_Segu : STRING; //INT i_modo_fb_Cria_Arquivo : INT; i_ErroID_fb_Escreve : INT; //UINT i_Tamanho_Palavra : UINT; // Blocos de Funcao fb_Cria_Arquivo : FILE.Open; fb_Fecha_Arquivo : FILE.Close; fb_Escreve : FILE.Write; // DATA E HORA sc_DataTime : SysTimeRtc.RTS_SYSTIMEDATE; // STRUCT = sc uidResultRtcGet : UDINT; //stGetDate : SysTimeRtc.RTS_SYSTIMEDATE; uidResultConvertToDate : UDINT; uidResult : RTS_IEC_RESULT; END_VAR VAR_INPUT //BOOL b_SET_Executa_fb_Cria_Arquivo : BOOL; b_RES_Executa_fb_Cria_Arquivo : BOOL; b_Executa_fb_Escrita : BOOL; // INT i_Linhas_Cabecalho : INT; i_Linhas_Cabecalho_Anterior : INT; END_VAR // Para Testes s_Nome_Arquivo := 'relatorio/Teste1M.CSV'; i_modo_fb_Cria_Arquivo := 0; // TRATAMENTO DE DATOS DATA e HORA uidResultRtcGet := SysTimeRtcGet(uidResult); uidResultConvertToDate := SysTimeRtcConvertUtcToDate(dwTimestampUtc := uidResultRtcGet, pDate := sc_DataTime); // FORMATA DATA EM UMA STRING s_Dia := UINT_TO_STRING(sc_DataTime.wDay); s_Mes := UINT_TO_STRING(sc_DataTime.wMonth); s_Ano := UINT_TO_STRING(sc_DataTime.wYear); s_Data_Atualizada_Formatada := CONCAT(s_Dia, CONCAT('/', CONCAT(s_Mes, CONCAT('/', s_Ano)))); // FORMATA HORA EM UMA STRING s_Hora := UINT_TO_STRING(sc_DataTime.wHour); s_Minu := UINT_TO_STRING(sc_DataTime.wMinute); s_Segu := UINT_TO_STRING(sc_DataTime.wSecond); s_Hora_Atualizada_Formatada := CONCAT(s_Hora, CONCAT(':', CONCAT(s_Minu, CONCAT(':', s_Segu)))); // ABRE e CRIA ARQUIVO IF (b_SET_Executa_fb_Cria_Arquivo OR b_Executa_fb_Cria_Arquivo) AND NOT(b_RES_Executa_fb_Cria_Arquivo) THEN b_Executa_fb_Cria_Arquivo := TRUE; ELSE b_Executa_fb_Cria_Arquivo := FALSE; END_IF fb_Cria_Arquivo( xExecute := b_Executa_fb_Cria_Arquivo, sFileName := s_Nome_Arquivo, eFileMode := i_modo_fb_Cria_Arquivo, xExclusive := FALSE, ); //i_Tamanho_Palavra := SizeOf(s_Palavra); // ESCREVE NO ARQUIVO fb_Escreve( xExecute := b_Executa_fb_Escrita, hFile := fb_Cria_arquivo.hFile, szSize := SIZEOF(s_Palavra), pBuffer := ADR(s_palavra) ); // FECHA ARQUIVO fb_Fecha_Arquivo( xExecute := b_Executa_fb_Fecha_Arquivo, hFile := fb_Cria_arquivo.hFile ); IF Criacao_Edicao_Arquivos.b_Escreve_Cabecalho THEN CASE i_Linhas_Cabecalho OF 1 : s_palavra := 'ESTERILIZADOR'; 2: s_palavra := 'MAQ4; $R$L'; 3: s_palavra := CONCAT(CONCAT('CLIENTE: ',GVL.sReceitaAtual), '$R$L'); 4: s_palavra := CONCAT(CONCAT('NUMERO LOTE: ',DINT_TO_STRING(GVL.Lote)), '$R$L'); 5: s_palavra := CONCAT(CONCAT('OPERADOR: ', DINT_TO_STRING(GVL.Operador)), '$R$L'); 6: s_palavra := CONCAT(CONCAT('DATA: ', s_Data_Atualizada_Formatada), '$R$L'); 7: s_palavra := CONCAT(CONCAT('HORA: ', s_Hora_Atualizada_Formatada), '$R$L'); 8: s_palavra := '$R$L'; 9: s_palavra := 'PARAMETROS AJUSTADOS $R$L'; 10: s_palavra := CONCAT('TEMPO PRE COND.: ', CONCAT(INT_TO_STRING(GVL.rc_T_PreCondicionamento), ' min $R$L')); 11: s_palavra := CONCAT('PULSOS INERTIZACAO 1: ', CONCAT(INT_TO_STRING(GVL.rc_PulsosInertizacao_1), ' PULSOS $R$L')); 12: s_palavra := CONCAT('PULSOS INERTIZACAO 2: ', CONCAT(INT_TO_STRING(GVL.rc_PulsosInertizacao_2), ' PULSOS $R$L')); 13: s_palavra := CONCAT('PULSOS AERACAO: ', CONCAT(INT_TO_STRING(GVL.rc_PulsosAeracao_2), ' PULSOS $R$L')); 14: s_palavra := CONCAT('PRESSAO DE VACUO 1: ', CONCAT(REAL_TO_STRING(GVL.rc_PV1_max), ' bar $R$L')); 15: s_palavra := CONCAT('PRESSAO DE VACUO 2: ', CONCAT(REAL_TO_STRING(GVL.rc_PV2), ' bar $R$L')); 16: s_palavra := CONCAT('PRESSAO INERTIZACAO 1: ', CONCAT(REAL_TO_STRING(GVL.rc_PN1), ' bar $R$L')); 17: s_palavra := CONCAT('PRESSAO INERTIZACAO 2: ', CONCAT(REAL_TO_STRING(GVL.rc_PN2), ' bar $R$L')); 18: s_palavra := CONCAT('PRESSAO AERACAO: ', CONCAT(REAL_TO_STRING(GVL.rc_PAR), ' bar $R$L')); 19: s_palavra := CONCAT('PRESSAO ETO: ', CONCAT(REAL_TO_STRING(GVL.rc_PressaoETO), ' bar $R$L')); 20: s_palavra := CONCAT('PRESSAO Compl.: ', CONCAT(REAL_TO_STRING(GVL.rc_PressaoETO_2), ' bar $R$L')); 21: s_palavra := CONCAT('MASSA ETO: ', CONCAT(REAL_TO_STRING(GVL.rc_QuantidadeETO), ' Kg $R$L')); 22: s_palavra := CONCAT('TEMPO PRE COND. Vacuo/Vapor: ', CONCAT(INT_TO_STRING(GVL.rc_T_Vapor_1), ' min $R$L')); 23: s_palavra := CONCAT('SET-POINT: ', CONCAT(REAL_TO_STRING(GVL.ihm_SPTemperatura), ' Β°C $R$L')); 24: s_palavra := CONCAT('TEMPO ESTERILIZACAO: ', CONCAT(INT_TO_STRING(GVL.rc_TempoEsterilizacao), ' min $R$L')); 25: s_palavra := CONCAT('TEMPO PARADA: ', CONCAT(INT_TO_STRING(GVL.rc_T_PressaoParada), ' min $R$L')); 26: s_palavra := CONCAT('UMIDADE: ', CONCAT(REAL_TO_STRING(GVL.rc_UmidadeRelativa), ' % $R$L')); 27: s_palavra := '$R$L'; END_CASE END_IF Note: The code I am attaching does not include the execution/sequence management of the function blocks. I have omitted this part to keep the example focused on the CSV writing routine. The function blocks are triggered and managed by a separate state machine. I am not sure what the correct approach is in this case, especially since the official documentation recommends using SIZEOF() for szSize. I will attach my code and a screenshot of the generated CSV showing the issue. Has anyone experienced something similar or could suggest the correct way to write a changing STRING to a CSV file using CAA.File.Write?
Last updated: 2026-09-08

Post by clesio on CAA.File.Write – Unexpected characters when writing a CSV file CODESYS Forge talk (Post)
Hello everyone, I am trying to create a routine in CODESYS to export a report to a CSV file using the CAA.File library. The file is created successfully, but I am getting some unexpected/unknown characters in the generated CSV. The problem seems to be related to the SIZEOF() function. I am using it for the szSize parameter of File.Write, as recommended in the CODESYS documentation. However, when I use SIZEOF(), the CSV contains extra characters and parts of previous strings. I also tried using LEN(), but in my application the file is created empty, even though the string and the calculated length appear to be correct. Here is the relevant part of my code: PROGRAM Relatorio VAR // Bool b_Executa_fb_Cria_Arquivo : BOOL; b_Executa_fb_Fecha_Arquivo : BOOL; b_Escreve_dados : BOOL; b_Erro_fb_Escreve : BOOL; //STR s_Nome_Arquivo : STRING; s_Palavra : STRING; s_Palavra_Anterior : STRING; s_Data_Atualizada_Formatada : STRING; s_Dia : STRING; s_Mes : STRING; s_Ano : STRING; s_Hora_Atualizada_Formatada : STRING; s_Hora : STRING; s_Minu : STRING; s_Segu : STRING; //INT i_modo_fb_Cria_Arquivo : INT; i_ErroID_fb_Escreve : INT; //UINT i_Tamanho_Palavra : UINT; // Blocos de Funcao fb_Cria_Arquivo : FILE.Open; fb_Fecha_Arquivo : FILE.Close; fb_Escreve : FILE.Write; // DATA E HORA sc_DataTime : SysTimeRtc.RTS_SYSTIMEDATE; // STRUCT = sc uidResultRtcGet : UDINT; //stGetDate : SysTimeRtc.RTS_SYSTIMEDATE; uidResultConvertToDate : UDINT; uidResult : RTS_IEC_RESULT; END_VAR VAR_INPUT //BOOL b_SET_Executa_fb_Cria_Arquivo : BOOL; b_RES_Executa_fb_Cria_Arquivo : BOOL; b_Executa_fb_Escrita : BOOL; // INT i_Linhas_Cabecalho : INT; i_Linhas_Cabecalho_Anterior : INT; END_VAR // Para Testes s_Nome_Arquivo := 'relatorio/Teste1M.CSV'; i_modo_fb_Cria_Arquivo := 0; // TRATAMENTO DE DATOS DATA e HORA uidResultRtcGet := SysTimeRtcGet(uidResult); uidResultConvertToDate := SysTimeRtcConvertUtcToDate(dwTimestampUtc := uidResultRtcGet, pDate := sc_DataTime); // FORMATA DATA EM UMA STRING s_Dia := UINT_TO_STRING(sc_DataTime.wDay); s_Mes := UINT_TO_STRING(sc_DataTime.wMonth); s_Ano := UINT_TO_STRING(sc_DataTime.wYear); s_Data_Atualizada_Formatada := CONCAT(s_Dia, CONCAT('/', CONCAT(s_Mes, CONCAT('/', s_Ano)))); // FORMATA HORA EM UMA STRING s_Hora := UINT_TO_STRING(sc_DataTime.wHour); s_Minu := UINT_TO_STRING(sc_DataTime.wMinute); s_Segu := UINT_TO_STRING(sc_DataTime.wSecond); s_Hora_Atualizada_Formatada := CONCAT(s_Hora, CONCAT(':', CONCAT(s_Minu, CONCAT(':', s_Segu)))); // ABRE e CRIA ARQUIVO IF (b_SET_Executa_fb_Cria_Arquivo OR b_Executa_fb_Cria_Arquivo) AND NOT(b_RES_Executa_fb_Cria_Arquivo) THEN b_Executa_fb_Cria_Arquivo := TRUE; ELSE b_Executa_fb_Cria_Arquivo := FALSE; END_IF fb_Cria_Arquivo( xExecute := b_Executa_fb_Cria_Arquivo, sFileName := s_Nome_Arquivo, eFileMode := i_modo_fb_Cria_Arquivo, xExclusive := FALSE, ); //i_Tamanho_Palavra := SizeOf(s_Palavra); // ESCREVE NO ARQUIVO fb_Escreve( xExecute := b_Executa_fb_Escrita, hFile := fb_Cria_arquivo.hFile, szSize := SIZEOF(s_Palavra), pBuffer := ADR(s_palavra) ); // FECHA ARQUIVO fb_Fecha_Arquivo( xExecute := b_Executa_fb_Fecha_Arquivo, hFile := fb_Cria_arquivo.hFile ); IF Criacao_Edicao_Arquivos.b_Escreve_Cabecalho THEN CASE i_Linhas_Cabecalho OF 1 : s_palavra := 'ESTERILIZADOR'; 2: s_palavra := 'MAQ4; $R$L'; 3: s_palavra := CONCAT(CONCAT('CLIENTE: ',GVL.sReceitaAtual), '$R$L'); 4: s_palavra := CONCAT(CONCAT('NUMERO LOTE: ',DINT_TO_STRING(GVL.Lote)), '$R$L'); 5: s_palavra := CONCAT(CONCAT('OPERADOR: ', DINT_TO_STRING(GVL.Operador)), '$R$L'); 6: s_palavra := CONCAT(CONCAT('DATA: ', s_Data_Atualizada_Formatada), '$R$L'); 7: s_palavra := CONCAT(CONCAT('HORA: ', s_Hora_Atualizada_Formatada), '$R$L'); 8: s_palavra := '$R$L'; 9: s_palavra := 'PARAMETROS AJUSTADOS $R$L'; 10: s_palavra := CONCAT('TEMPO PRE COND.: ', CONCAT(INT_TO_STRING(GVL.rc_T_PreCondicionamento), ' min $R$L')); 11: s_palavra := CONCAT('PULSOS INERTIZACAO 1: ', CONCAT(INT_TO_STRING(GVL.rc_PulsosInertizacao_1), ' PULSOS $R$L')); 12: s_palavra := CONCAT('PULSOS INERTIZACAO 2: ', CONCAT(INT_TO_STRING(GVL.rc_PulsosInertizacao_2), ' PULSOS $R$L')); 13: s_palavra := CONCAT('PULSOS AERACAO: ', CONCAT(INT_TO_STRING(GVL.rc_PulsosAeracao_2), ' PULSOS $R$L')); 14: s_palavra := CONCAT('PRESSAO DE VACUO 1: ', CONCAT(REAL_TO_STRING(GVL.rc_PV1_max), ' bar $R$L')); 15: s_palavra := CONCAT('PRESSAO DE VACUO 2: ', CONCAT(REAL_TO_STRING(GVL.rc_PV2), ' bar $R$L')); 16: s_palavra := CONCAT('PRESSAO INERTIZACAO 1: ', CONCAT(REAL_TO_STRING(GVL.rc_PN1), ' bar $R$L')); 17: s_palavra := CONCAT('PRESSAO INERTIZACAO 2: ', CONCAT(REAL_TO_STRING(GVL.rc_PN2), ' bar $R$L')); 18: s_palavra := CONCAT('PRESSAO AERACAO: ', CONCAT(REAL_TO_STRING(GVL.rc_PAR), ' bar $R$L')); 19: s_palavra := CONCAT('PRESSAO ETO: ', CONCAT(REAL_TO_STRING(GVL.rc_PressaoETO), ' bar $R$L')); 20: s_palavra := CONCAT('PRESSAO Compl.: ', CONCAT(REAL_TO_STRING(GVL.rc_PressaoETO_2), ' bar $R$L')); 21: s_palavra := CONCAT('MASSA ETO: ', CONCAT(REAL_TO_STRING(GVL.rc_QuantidadeETO), ' Kg $R$L')); 22: s_palavra := CONCAT('TEMPO PRE COND. Vacuo/Vapor: ', CONCAT(INT_TO_STRING(GVL.rc_T_Vapor_1), ' min $R$L')); 23: s_palavra := CONCAT('SET-POINT: ', CONCAT(REAL_TO_STRING(GVL.ihm_SPTemperatura), ' Β°C $R$L')); 24: s_palavra := CONCAT('TEMPO ESTERILIZACAO: ', CONCAT(INT_TO_STRING(GVL.rc_TempoEsterilizacao), ' min $R$L')); 25: s_palavra := CONCAT('TEMPO PARADA: ', CONCAT(INT_TO_STRING(GVL.rc_T_PressaoParada), ' min $R$L')); 26: s_palavra := CONCAT('UMIDADE: ', CONCAT(REAL_TO_STRING(GVL.rc_UmidadeRelativa), ' % $R$L')); 27: s_palavra := '$R$L'; END_CASE END_IF Note: The code I am attaching does not include the execution/sequence management of the function blocks. I have omitted this part to keep the example focused on the CSV writing routine. The function blocks are triggered and managed by a separate state machine. I am not sure what the correct approach is in this case, especially since the official documentation recommends using SIZEOF() for szSize. I will attach my code and a screenshot of the generated CSV showing the issue. Has anyone experienced something similar or could suggest the correct way to write a changing STRING to a CSV file using CAA.File.Write?
Last updated: 2026-09-08

<< < 1 .. 8 9 10 (Page 10 of 10)

Showing results of 238

Sort by relevance or date