-> Hier kostenlos registrieren
Folge dem Video um zu sehen, wie unsere Website als Web-App auf dem Startbildschirm installiert werden kann.
Anmerkung: Diese Funktion ist in einigen Browsern möglicherweise nicht verfügbar.
Das ist dann gegenüber Beckhoff TwinCAT und anderen Codesys Derivaten echt ein Pluspunkt, da sind String Funktionen immer auf 255 Zeichen begrenzt.Wo steht das? Geht bei mir problemlos und ist ja Sinn und Zweck der Parameter.
Anhang anzeigen 78170
// split a string in CSV-format into an array of data values
// return TRUE on success, FALSE on CSV format error
// 2024-05-17 KLM created
FUNCTION FC_ExtractCsvData : BOOL
VAR CONSTANT
c_bSeparator_LF : BYTE := WagoTypes.eChar.LineFeed;
END_VAR
VAR_INPUT
ptsRawString : POINTER TO STRING(1);
udiSize : UDINT;
bDTFormat : BYTE; // time stamp format - see WagoAppString.FuStringToDT.bFormat, e.g. 3: 'dd.mm.jjjj HH:MM'
bSeparator_Data : BYTE := WagoTypes.eChar.Semicolon;
xHeaderIsIncluded : bool;
END_VAR
VAR_IN_OUT
atypResultData : ARRAY[*] OF typDataSet;
END_VAR
VAR_OUTPUT
xOverFlow : BOOL; // more line in CSV then array fields
diNumOfDataLines : DINT;
END_VAR
VAR
sExtraction_Line : WagoAppString.MaxString;
sExtraction_Value : WagoAppString.MaxString;
udiExtractIndex_Line : UDINT;
udiExtractIndex_Value : UDINT;
diOutputIndex : DINT;
usiDataIndex : USINT;
udiLen_Line : UDINT;
ptbChar : POINTER TO BYTE;
xIsHeaderLine : bool;
END_VAR
----------------------------------------------
diOutputIndex := LOWER_BOUND(atypResultData, 1) - 1;
diNumOfDataLines := 0;
udiExtractIndex_Line := 1;
WHILE (udiExtractIndex_Line <> 0) DO
xIsHeaderLine := xHeaderIsIncluded AND (udiExtractIndex_Line = 1);
sExtraction_Line := WagoAppString.StrExtractToken(
sBuffer := ptsRawString^,
udiSize := udiSize,
bSeparator := c_bSeparator_LF,
udiBegin := udiExtractIndex_Line,
udiNext => udiExtractIndex_Line);
// skip header
IF xIsHeaderLine THEN
CONTINUE;
END_IF
// cut most right padded '$R' (CarriageReturn)
udiLen_Line := WagoAppString.StrLength(
sBuffer := sExtraction_Line,
udiSize := SIZEOF(sExtraction_Line));
ptbChar := ADR(sExtraction_Line) + udiLen_Line - 1;
IF ptbChar^ = WagoTypes.eChar.CarriageReturn THEN
ptbChar^ := 0; // override by string termination char
END_IF
// CSV line to data array index
diOutputIndex := diOutputIndex + 1;
IF diOutputIndex > UPPER_BOUND(atypResultData, 1) THEN
xOverFlow := TRUE;
FC_ExtractCsvData := TRUE;
RETURN;
END_IF
udiExtractIndex_Value := 1;
usiDataIndex := 0;
WHILE (udiExtractIndex_Value <> 0) DO
sExtraction_Value := WagoAppString.StrExtractToken(
sBuffer := sExtraction_Line,
udiSize := udiSize,
bSeparator := bSeparator_Data,
udiBegin := udiExtractIndex_Value,
udiNext => udiExtractIndex_Value);
usiDataIndex := usiDataIndex + 1;
CASE usiDataIndex OF
1: // time stamp
atypResultData[ diOutputIndex ].dtTimeStamp := WagoAppString.FuStringToDT(bFormat:=bDTFormat, sInput:= sExtraction_Value);
IF atypResultData[ diOutputIndex ].dtTimeStamp = TO_DT(0) THEN
JMP FORMAT_ERROR;
END_IF
2: // 1st value
atypResultData[ diOutputIndex ].rValue_1 := TO_REAL(sExtraction_Value);
3: // 2nd value
JMP FORMAT_ERROR;
4: // 3td value
JMP FORMAT_ERROR;
ELSE
JMP FORMAT_ERROR;
END_CASE
END_WHILE
diNumOfDataLines := diNumOfDataLines + 1;
END_WHILE
xOverFlow := FALSE;
FC_ExtractCsvData := TRUE;
RETURN;
FORMAT_ERROR:
FC_ExtractCsvData := FALSE;
// split a string in CSV-format into an array of data values
// return TRUE on success, FALSE on CSV format error
// use this function for format:
// jjjj-mm-dd, HH:MM, value, value, value, ...
// 2024-05-17 KLM created
FUNCTION FC_ExtractCsvData_D_T : BOOL
VAR CONSTANT
c_bSeparator_LF : BYTE := WagoTypes.eChar.LineFeed;
c_bSeparator_Data : BYTE := WagoTypes.eChar.Comma;
END_VAR
VAR_INPUT
ptsRawString : POINTER TO STRING(1);
udiSize : UDINT;
xHeaderIsIncluded : BOOL;
END_VAR
VAR_IN_OUT
atypResultData : ARRAY[*] OF typDataSet;
END_VAR
VAR_OUTPUT
xOverFlow : BOOL; // more line in CSV then array fields
diNumOfDataLines : dint;
END_VAR
VAR
sExtraction_Line : WagoAppString.MaxString;
sExtraction_Value : WagoAppString.MaxString;
udiExtractIndex_Line : UDINT;
udiExtractIndex_Value : UDINT;
diOutputIndex : DINT;
usiDataIndex : USINT;
udiLen_Line : UDINT;
ptbChar : POINTER TO BYTE;
xIsHeaderLine : bool;
dDate : DATE;
todTime : TOD;
END_VAR
---------------------------------------------
diOutputIndex := LOWER_BOUND(atypResultData, 1) - 1;
diNumOfDataLines := 0;
udiExtractIndex_Line := 1;
WHILE (udiExtractIndex_Line <> 0) DO
xIsHeaderLine := xHeaderIsIncluded AND (udiExtractIndex_Line = 1);
sExtraction_Line := WagoAppString.StrExtractToken(
sBuffer := ptsRawString^,
udiSize := udiSize,
bSeparator := c_bSeparator_LF,
udiBegin := udiExtractIndex_Line,
udiNext => udiExtractIndex_Line);
// skip header
IF xIsHeaderLine THEN
CONTINUE;
END_IF
// cut most right padded '$R' (CarriageReturn)
udiLen_Line := WagoAppString.StrLength(
sBuffer := sExtraction_Line,
udiSize := SIZEOF(sExtraction_Line));
ptbChar := ADR(sExtraction_Line) + udiLen_Line - 1;
IF ptbChar^ = WagoTypes.eChar.CarriageReturn THEN
ptbChar^ := 0; // override by string termination char
END_IF
// CSV line to data array index
diOutputIndex := diOutputIndex + 1;
IF diOutputIndex > UPPER_BOUND(atypResultData, 1) THEN
xOverFlow := TRUE;
FC_ExtractCsvData_D_T := TRUE;
RETURN;
END_IF
udiExtractIndex_Value := 1;
usiDataIndex := 0;
WHILE (udiExtractIndex_Value <> 0) DO
sExtraction_Value := WagoAppString.StrExtractToken(
sBuffer := sExtraction_Line,
udiSize := udiSize,
bSeparator := c_bSeparator_Data,
udiBegin := udiExtractIndex_Value,
udiNext => udiExtractIndex_Value);
usiDataIndex := usiDataIndex + 1;
CASE usiDataIndex OF
1: // time stamp: date as jjjj-mm-dd
dDate := FuStringToDate(bFormat:= 0, sInput:= sExtraction_Value);
IF dDate = TO_DATE(0) THEN
JMP FORMAT_ERROR;
END_IF
2: // time stamp: time as HH:MM
todTime := FuStringToTod(sInput:= sExtraction_Value);
IF todTime= TO_TOD(0) THEN
JMP FORMAT_ERROR;
END_IF
atypResultData[ diOutputIndex ].dtTimeStamp := FuSetDT(datDate:= dDate, tdTimeOfDay:= todTime);
3: // 1st value
atypResultData[ diOutputIndex ].rValue_1 := TO_REAL(sExtraction_Value);
4: // 2nd value
JMP FORMAT_ERROR;
5: // 3td value
JMP FORMAT_ERROR;
ELSE
JMP FORMAT_ERROR;
END_CASE
END_WHILE
diNumOfDataLines := diNumOfDataLines + 1;
END_WHILE
xOverFlow := FALSE;
FC_ExtractCsvData_D_T := TRUE;
RETURN;
FORMAT_ERROR:
FC_ExtractCsvData_D_T := FALSE;
WAGO verwendet die native CODESYS, womit die Standard String-Funktionen auch auf 255 Zeichen begrenzt sind. Aber in der Bibliothek WagoAppString sind Funktionen, die keine Limitierung haben und welche, die über den änderbaren Parameter begrenzt sind.Das ist dann gegenüber Beckhoff TwinCAT und anderen Codesys Derivaten echt ein Pluspunkt, da sind String Funktionen immer auf 255 Zeichen begrenzt.
Danke, habe jetzt mal einen Blick in die Doku geworfen. Echt genial das Teil, das da keiner vorher auf die Idee gekommen ist.WAGO verwendet die native CODESYS, womit die Standard String-Funktionen auch auf 255 Zeichen begrenzt sind. Aber in der Bibliothek WagoAppString sind Funktionen, die keine Limitierung haben und welche, die über den änderbaren Parameter begrenzt sind.
Ja, die sind super. Stecken in jedem zweiten Projekt von mirAuch die Befehlsvarianten, zum Beispiel für CONCAT mit 3, 4, 5, usw Strings sind klasse.
ptsText : POINTER TO STRING(1);
ptabASCII : POINTER TO ARRAY[0..1024] OF BYTE;
-----------------
ptsText := ADR(sText);
ptabASCII := ptsText;
Zeig doch bitte einmal Deinen Aufruf von StrExtractToken. Laut Doku ist die einzige Begrenzung, dass das zu findende Token nicht größer als 254 Zeichen sein darf.Vielen Dank erst mal.
die WagoTypes.eChar sind Klasse, nur konnte ich keinen Fehler inder csv finden.
Der Fehler liegt immer noch in MAX_STRING_LENGTH. Den hab ich auf 2002 geändert.
WagoAppString.StrExtractToken sucht aber nur bis 255.
Er verwendet die FOR-Schleife. Die WHILE-Schleife ist mein Vorschlag. Und ja, das sollte einem bewusst sein, aber gerade bei STRING-Verarbeitungen ist die WHILE immer wieder recht nützlich.Die WHILE Schleife ist sehr bedenklich. Ein SPS-Programm wird zyklisch ausgeführt, wenn die WHILE Schleife also zu lange braucht kommt es zu Problemen.
Gibt es eventuell eine Fehlermeldung?
Irgendwo im Extrahieren der Teil-STRINGs ist ein Fehler bei der Positionsberechnung. Habe den Fehler aber auch nicht gesucht, weil die StrExtractToken das sowieso schon macht. Habe den Teil einfach rausgeschmissen.Ab 13 fehlen die Zeichen
PROGRAM prgKLM
VAR
sFileName : STRING := '/media/sd/forecast.csv';
oReadCfg : WagoAppFileDir.FbReadWholeFile_cpt;
sReadErrorInfo : STRING;
xRead : BOOL;
xReadError : BOOL;
sCfg : STRING(2000);
sLine : STRING;
sDate : STRING;
sTime : STRING;
sValue : STRING;
// oToken_sperator : WagoAppString.StrExtractToken;
udiPosStart_Zeile : UDINT;
udiPosNext_Zeile : UDINT;
udiPosStart_Time : UDINT;
udiPosStart_Value : UDINT;
sZeileEnde : STRING := '$R$N';
iZeile : INT;
iAnzahlZeilen_in : INT := 18;
xLadeVisuVonCsv : BOOL;
END_VAR
(*
1. WagoAppFileDir.FbReadWholeFile_cpt
Den Eingang udiRxBufferSize belegst Du mit einem ADR(sReadBuffer) String in ausreichender Größe, z.B. STRING(2048).
Der Eingang udiRxNBytes ist dann nur diese Größe, also SIZEOF(sReadBuffer).
2. WagoAppString.StrExtractToken in einer While-Schleife.
Hiermit extrahierst Du erst eine ganze Zeile der CSV indem Du als bSeparator WagoTypes.eChar.LineFeed verwendest.
Dann nimmst Du die die gleiche Funktion um Zeitstempel und den Prozesswert zu trennen, also als bSeparator WagoTypes.eChar.Semicolon.
3. Umwandeln des Zeitstempels und in ein Array deines Datentyps aus DT und INT ablegen.
Umwandlung z.B. mit WagoAppString.FuStringToDT(bFormat:=3, sInput:= '02.05.2024 01:15')
*)
----------------------------------------------------
oReadCfg(
xTrigger := xRead,
sName := sFileName,
pRxBuffer := ADR(sCfg),
udiRxBuffersize := SIZEOF(sCfg) );
sReadErrorInfo := oReadCfg.oStatus.GetDescription();
// Simulation
sCfg := gvlSimulation.sCsvContent;
IF xLadeVisuVonCsv THEN
udiPosStart_Zeile := 1;
FOR iZeile := 1 TO iAnzahlZeilen_in DO // immer statische Anzahl Zeilen - was wenn es mehr oder weniger in der CSV sind?!
// CSV Zeile einlesen bis Umbruch
sLine := WagoAppString.StrExtractToken(
sBuffer := sCfg,
udiSize := SIZEOF(sCfg),
bSeparator := eChar.LineFeed,
udiBegin := udiPosStart_Zeile,
udiNext => udiPosNext_Zeile);
// nicht erforderlich, da bereits als Return von StrExtractToken verfügbar
//sLine := MID(sCfg, TO_INT(udiNextCSV - udiStartCSV), TO_INT(udiStartCSV)); // Text aus Zeile in String zum weiterzuverarbeiten
// Datum bis ertses Komma in Zeile
sDate := WagoAppString.StrExtractToken(
sBuffer := sLine,
udiSize := SIZEOF(sLine),
bSeparator := eChar.Comma,
udiBegin := 1,
udiNext => udiPosStart_Time);
// nicht erforderlich, da bereits als Return von StrExtractToken verfügbar
// udiDatumEnde := udiNextZ;
// sDate := MID(sLine, UDINT_TO_INT( udiNextZ -2 ), UDINT_TO_INT(udiStartZ ));
// Datum-STRING in DATE wandeln und in Array speichern (Zwischenvariable nicht erforderlich)
sDate := CONCAT('D#',sDate);
GVL.ar_zeit_soll_visu[iZeile].d_DATE_0 := TO_DATE(sDate);
// Uhrzeit zwischen 1. und 2. Komma
sTime := WagoAppString.StrExtractToken(
sBuffer := sLine,
udiSize := SIZEOF(sLine),
bSeparator := eChar.Comma,
udiBegin := udiPosStart_Time,
udiNext => udiPosStart_Value) ;
// nicht erforderlich, da bereits als Return von StrExtractToken verfügbar
// udiZeitEnde := udiNextZ;
// sTime := MID(sLine, UDINT_TO_INT(udiNextZ - udiDatumEnde -1 ), UDINT_TO_INT(udiDatumEnde)); // -1 wegen Komma
// Zeit-STRING in TIME_OF_DAY wandeln und in Array speichern (Zwischenvariable nicht erforderlich)
sTime := CONCAT('TOD#',sTime);
GVL.ar_zeit_soll_visu[iZeile].dt_time_0 := TO_TOD(sTime);
// Wert zwischen Zeit Ende und Zeilenende
sValue := WagoAppString.StrExtractToken(
sBuffer := sLine,
udiSize := SIZEOF(sLine),
bSeparator := TO_BYTE(sZeileEnde),
udiBegin := udiPosStart_Value,
udiNext => );
// nicht erforderlich, da bereits als Return von StrExtractToken verfügbar
// sValue := MID(sLine, UDINT_TO_INT(udiNextZv - udiZeitEnde ), UDINT_TO_INT(udiZeitEnde)); // Länge des Wertes spielt keine Rolle - bis ENDE Zeiel
// rArbeit:= FPU.StrToReal(sValue); // STRING ZU Real
// Wert-STRING in REAL wandeln und ins ARRAY VISU speichern (Zwischenvariable nicht erforderlich)
GVL.ar_zeit_soll_visu[iZeile].r_arbeit := TO_REAL(sValue);
udiPosStart_Zeile := udiPosNext_Zeile;
END_FOR // CSV Zeile
xLadeVisuVonCsv := FALSE;
END_IF
gvlSimulation
--------------------
{attribute 'qualified_only'}
VAR_GLOBAL
sCsvContent : STRING(3000) :=
'2024-05-11,04:00,0.1
2024-05-11,05:00,0.2
2024-05-11,06:00,0.3
2024-05-11,07:00,0.4
2024-05-11,08:00,8
2024-05-11,09:00,9
2024-05-11,10:00,10
2024-05-11,11:00,11
2024-05-11,12:00,12
2024-05-11,13:00,13
2024-05-11,14:00,14
2024-05-11,15:00,15
2024-05-11,16:00,16
2024-05-11,17:00,17
2024-05-11,18:00,18
2024-05-11,19:00,19
2024-05-11,20:00,20
2024-05-11,21:00,21';
END_VAR
Wir verwenden essentielle Cookies, damit diese Website funktioniert, und optionale Cookies, um den Komfort bei der Nutzung zu verbessern.
Siehe weitere Informationen und konfiguriere deine Einstellungen