diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae0f2e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +._AnjlabCodeBox.xml \ No newline at end of file diff --git a/AnjLabCodeBox.xml b/AnjLabCodeBox.xml new file mode 100644 index 0000000..eb119c9 --- /dev/null +++ b/AnjLabCodeBox.xml @@ -0,0 +1,3242 @@ + + + + + Comprehensive PLC Utility Library for CoDeSys + + + + + + + + + + + + + + + Sergey A Glukhov + true + Anjlab + ACB + Comprehensive PLC Utility Library for CoDeSys + reStructuredText + 888cbf53-45a1-47de-9a2a-fbd72e84a196 + false + qualified-access-only + AnjlabCodeBox + AnjLabCodeBox + false + https://github.com/anjlab/codebox + false + Anjlab Code Box + 3.5.17.1 + + + + + + + + + + + + + + + + + Input signal + + + + + + + + + + Output signal + + + + + + + + + Start time + + + + + + + + Signal memory + + + + + Discrete signal smoothing + + + + + IF input AND NOT memory THEN + startTime := TIME(); +END_IF; +memory := input; +output := input AND ((TIME() - startTime) > T#100ms); + + + + + aeabac3a-edf8-4a8d-bfc5-f6579f1a4daf + + + + + + + + + + + + Input signal + + + + + + + + + + Output signal + + + + + + + + + Start time + + + + + + + + Signal memory + + + + + Smoothing of discrete signal + + + + + IF (NOT input AND memory) OR (input AND NOT memory) THEN + startTime := TIME(); +END_IF; +memory := input; +output := input; +IF ((TIME() - startTime) < T#100ms) THEN + output := NOT input; +END_IF + + + + + ed6dd414-5d7c-46cd-a3e2-dfead0de1e5d + + + + + + + + + + + + Input signal + + + + + + + + + + Output signal + + + + + + + + + Start time + + + + + + + + Signal memory + + + + + Smoothing of discrete signal + + + + + IF NOT input AND memory THEN + startTime := TIME(); +END_IF; +memory := input; +output := input; +IF NOT input AND ((TIME() - startTime) < T#100ms) THEN + output := TRUE; +END_IF + + + + + 1185b75f-604d-4aa1-a1ea-6afa899718f0 + + + + + + + + + + + + Input signal + + + + + + + + + + Rising edge + + + + + + + + Falling edge + + + + + + + + + + + + + + + Filtered input + + + + + + + + + + Detects rising and falling edges with a 100 millisecond filter + + + + + fbDio1(input := input, output => filteredInput); +raise := fall := FALSE; +IF filteredInput AND NOT memory THEN + raise := TRUE; +ELSIF NOT filteredInput AND memory THEN + fall := TRUE; +END_IF +memory := filteredInput; + + + + + f0c530e2-1802-4730-a7c1-9e5a02094132 + + + + + + + + + + + + Input signal + + + + + + + + + + Output signal + + + + + + + + + Start time + + + + + + + + Signal memory + + + + + Double click + + + + + output := FALSE; +IF input AND NOT memory THEN + IF (TIME() - startTime) > T#100MS AND (TIME() - startTime) < T#800MS THEN + output := TRUE; + END_IF; + startTime := TIME(); +END_IF; +memory := input; + + + + + d6c942b4-f877-45a8-aaa5-9840654778e6 + + + + + + + + + + + + Input value + + + + + + + + Buffer size minimum 32 maximum 1000 + + + + + + + + Reset buffer + + + + + + + + + + Filtered output value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This function stores up to 32 values in a buffer and outputs the average value. You can decide how many values to store to get the average number. + + + + + // limit the bufferSize +bufferSize := MIN(MAX(bufferSize, 1000), 32); +// Initialize +IF NOT isInitialized OR reset THEN + isInitialized := TRUE; + FOR counter := 1 TO bufferSize DO + buffer[counter] := input; + END_FOR; + sum := input * bufferSize; + output := input; + RETURN; +END_IF; + +IF counter >= bufferSize THEN + counter := 1; +ELSE + counter := counter + 1; +END_IF + +sum := sum + input - buffer[counter]; +{warning disable C0195} +output := DWORD_TO_WORD(sum / bufferSize); +buffer[counter] := input; + + + + + 216bb070-e763-45f3-ab22-f6ba447a7047 + + + + + + + + + + + + Input value + + + + + + + Time interval + + + + + + + + + + Filtered output value + + + + + + + + + + + + + + + + + + + + + + + + + + + Returns the average value over a specified time interval. + + + + + currentTime := TIME_TO_DWORD(TIME()); +// Initialize +IF NOT isInitialized OR timeInterval = T#0s THEN + isInitialized := TRUE; + lastTime := currentTime; + output := input; +ELSIF output = input THEN + lastTime := currentTime; +ELSE + tempTime := WORD_TO_DWORD(input - output) * (currentTime - lastTime) / + TIME_TO_DWORD(timeInterval); + IF tempTime <> 0 THEN + output := DINT_TO_WORD(WORD_TO_DINT(output) + DWORD_TO_DINT(tempTime)); + lastTime := currentTime; + END_IF; +END_IF; + + + + + b0642b57-9ee0-4a1f-a6aa-0d37014eaf0e + + + + + + + + + + + + Input signal + + + + + + + Press time + + + + + + + + + + Output signal + + + + + + + + + Start time + + + + + + + + Signal memory + + + + + Long click + + + + + IF input AND NOT memory THEN + startTime := TIME(); +END_IF; +memory := input; +output := input AND ((TIME() - startTime) > pressTime); + + + + + 65c343db-a1af-4db4-a7ad-a65316c3f838 + + + + + + + + + + + + Start generator + + + + + + + + + + Pulse time + + + + + + + + + + Output signal + + + + + + + + + + + + + + + + + + + + Creates pulses with specified frequency + PwmClock(enable := TRUE, pulseTime := T#5ms); Output Q will have one PLC cycle pulse every 5 milliseconds. + + + + + currentTime := TIME(); +IF enable AND NOT memory THEN + lastTime := currentTime - pulseTime; +END_IF; +memory := enable; +output := currentTime - lastTime >= pulseTime; +IF output THEN lastTime := currentTime; END_IF; + + + + + 69fbfb76-4eb7-4d83-95c1-21404034352c + + + + + + + + + + + + Frequency, times per second (Hz) + + + + + + + + Duty cycle + + + + + + + + + + Output signal + + + + + + + + + + + + + + + + + + + + + + creates a PWM signal of a given frequency with a ratio offset. Each PWM cycle can be divided into 2 phases: Ton (on time) and Toff (off time). + The ratio is set from 0 to 1. For example, a ratio of 0.5 will divide the time of each PWM cycle into 50% for Ton and 50% for Toff . + + + + + // If the PWM frequency is 0, then we interrupt the execution of the FB +IF frequency <= 0.0 THEN + output := FALSE; + RETURN; +END_IF; +// Calculate how many milliseconds are required for one PWM cycle +tempValue := 1000.0 / frequency; +// Launching the signal generator +clock(pulseTime := REAL_TO_TIME(tempValue)); +// We create a sustained pulse for the required time +pulseTimer(in := clock.output, pt := REAL_TO_TIME(tempValue * dutyCycle), Q => output); + + + + + b2c549b4-3ee4-4a60-a429-c5dda1b73017 + + + + + + + + + + + + Frequency (Hz) + + + + + + + Ton phase length + + + + + + + + + + Output signal + + + + + + + + + + + + + + + + + Creates PWM signal with specified Ton phase time. For example, we can set F to 100 times per second. This means one PWM cycle will be 10 + milliseconds. Now if we set PW to T#5ms, we'll get equal time for Ton and Toff phases at 50%. + + + + + // If the PWM frequency is 0 or the time of one cycle is less than the time for the Ton phase, then we interrupt the execution of the FB +IF frequency <= 0.0 OR (REAL_TO_TIME(1000.0 / frequency) < pulseWidth) THEN + output := FALSE; + RETURN; +END_IF; +// Start the signal generator +clock(pulseTime := REAL_TO_TIME(1000.0 / frequency)); +// We create a sustained pulse for the required time +pulseTimer(in := clock.output, PT := pulseWidth, Q => output); + + + + + 453b2034-7959-4690-aff2-788eefb570f6 + + + + + + + + + + + + Latitude + + + + + + + + Longitude + + + + + + + + Current time in UTC + + + + + + + + + + + Degree above horizon at which sunset or sunrise is considered to occur + + + + + + + + + + Time of solar noon + + + + + + + + Sunrise time + + + + + + + + Sunset time + + + + + + + + Sun declination angle at solar noon in degrees + + + + + + + + + + Sun declination angle at noon in radians + + + + + + + Time delta from solar noon to sunrise or sunset + + + + + + + + + + Calculate sunset and sunrise times + + + + + midday := SunMidday(longitude, currentTime); +b := latitude * 0.0174532925199433; +dk := 0.40954 * SIN(0.0172 * (UINT_TO_REAL(DayOfYear(currentTime)) - 79.35)); +sunDeclination := DegreesToRadians(DK); +IF sunDeclination > 180.0 THEN + sunDeclination := sunDeclination - 360.0; +END_IF; +sunDeclination := 90.0 - latitude + sunDeclination; +delta := HourToTime(REAL_TO_INT(ACOS((SIN(RadiansToDegrees(h)) - SIN(b) * SIN(dk)) / (COS(b) * COS(dk))) * 3.819718632)); +sunRise := midday - delta; +sunSet := midday + delta; + + + + + cce3bab6-f4dc-4237-acc9-88ac792521e0 + + + + + + + + + + + + + + + IP address as string, for example, '192.168.1.2' + + + + + + + + + + + + Convert IP address to hexadecimal format + + + + + position := FIND(ipStr, '.'); +WHILE position > 0 DO + IpDecode := SHL(IpDecode, 8) OR + STRING_TO_DWORD(LEFT(ipStr, position - 1)); + ipStr := DELETE(ipStr, position, 1); + position := FIND(ipStr, '.'); +END_WHILE; +IpDecode := SHL(IpDecode, 8) OR STRING_TO_DWORD(ipStr); + + + + + 5816bee8-262c-48e8-8b13-2203371ffb56 + + + + + + + + + + + + + + + Input value + + + + + Rounds the value and returns the nearest integer value that is greater than or equal to X. + ceil(3.14) = 4 ceil(-3.14) = -3 + + + + + Ceil := REAL_TO_DINT(input); + +IF DINT_TO_REAL(Ceil) < input THEN + Ceil := Ceil + 1; +END_IF; + + + + + 78816cb7-8bcc-46ba-a32f-d1dc6fe2b231 + + + + + + + + + + + + + + + Input value + + + + + Calculates with base 10 to the power. + exp10(2) = 100 exp10(3) = 1000 + + + + + Exp10 := EXP(input * 2.30258509299405); + + + + + e1550196-3879-4553-be95-49fb41feaf24 + + + + + + + + + + + + + + + Input value + + + + + + + + Power + + + + + + + + + + + + Raising to power X^N. Although there is an EXPT operator in the standard, according to OSCAT, this algorithm works 30 times faster in CoDeSys. + + + + + sign := power.15; +power := ABS(power); +IF power.0 THEN ExpN := input; ELSE ExpN := 1.0; END_IF; +power := SHR(power, 1); + +WHILE power > 0 DO + input := input * input; + IF power.0 THEN ExpN := ExpN * input; END_IF; + power := SHR(power, 1); +END_WHILE; + +IF sign THEN ExpN := 1.0 / ExpN; END_IF; + + + + + 640185c1-fa56-49b9-b0b7-33d16ee89099 + + + + + + + + + + + + + + + Input value + + + + + Rounds the value and returns the nearest integer value that is less than or equal to X. + floor(3.14) = 3 floor(-3.14) = -4 + + + + + FLOOR := REAL_TO_DINT(input); +IF DINT_TO_REAL(FLOOR) > input THEN + FLOOR := FLOOR - 1; +END_IF; + + + + + c7ae1511-9cb2-442c-a360-58c1601418fa + + + + + + + + + + + + + + + Input value + + + + + This function returns the fractional part of a floating point number. + fract(3.14) = 0.14 + + + + + IF ABS(input) < 2.0E9 THEN + FRACT := input - DINT_TO_REAL(DTrunc(input)); +ELSE + FRACT := 0.0; +END_IF; + + + + + 2c97c99b-94d7-41a3-88c0-c889ffc9588e + + + + + + + + + + + + + + + Dividend + + + + + + + + Divisor + + + + + Calculates the remainder of division for floating point numbers. + modr(5.5, 2.5) = 0.5 + + + + + IF divisor = 0.0 THEN + MODR := 0.0; +ELSE + MODR := input - DINT_TO_REAL(FLOOR(input / divisor)) * divisor; +END_IF; + + + + + + 66e67209-b916-448f-8711-f1d9c196c26f + + + + + + + + + + + + + + + Number to process + + + + + + + + Decimal precision + + + + + Rounds a floating point number to the specified number of decimal places. + round(12.23456789, 2) = 12.23 + + + + + Round := DWORD_TO_REAL(REAL_TO_DWORD(input * Exp10(precision))) / Exp10(precision); + + + + + + 9884e99a-0864-45b3-bf27-509db44c9794 + + + + + + + + + + + + + + + Input value in Celsius + + + + + Convert temperature from Celsius to Fahrenheit + + + + + CelciusToFarenheit := celsiusTemp * 1.8 + 32.0; + + + + + 7f5d796b-74a9-421e-bd0e-9267c5a44f76 + + + + + + + + + + + + + + + Input value in Celsius + + + + + + + + + + + + + 0°C in Kelvin + + + + + Convert temperature from Celsius to Kelvin + + + + + CelciusToKelvin := celsiusTemp + physT0; + + + + + 0b238ef8-5d8d-4a08-8ea0-8b0731782a91 + + + + + + + + + + + + + + + Input value in radians + + + + + Converts radians to degrees. + + + + + DegreesToRadians := ModR(57.29577951308232 * radians, 360.0); + + + + + 4981396d-e136-4432-9a71-2aaee415565e + + + + + + + + + + + + + + + Input value in Fahrenheit + + + + + Convert temperature from Fahrenheit to Celsius + + + + + FarenheitToCelcius := (fahrenheitTemp - 32.0) * 0.5555555555555; + + + + + e90ecedc-3b3d-421c-9673-f59db8c05ffd + + + + + + + + + + + + + + + Input value in Kelvin + + + + + + + + + + + + + 0°C in Kelvin + + + + + Convert temperature from Kelvin to Celsius + + + + + KelvinToCelcius := kelvinTemp - phys_T0; + + + + + e1affddf-5581-4627-8d61-7f2a7f2ca0fa + + + + + + + + + + + + + + + Input value in Km/h + + + + + Speed conversion: km/h to m/s + + + + + KmhToMs := kmhSpeed * 0.2777777777777; + + + + + b26a4465-6ef2-4dc8-b751-aebd4e2f2f56 + + + + + + + + + + + + + + + Input value in m/s + + + + + Speed conversion: m/s to km/h + + + + + MsToKmh := msSpeed * 3.6; + + + + + 21dd1116-5d67-493d-8c77-5b380095d556 + + + + + + + + + + + + + + + Input value in degrees + + + + + + + + + + + + + + + + + + + + + + + + + + + Converts degrees to radians + + + + + RadiansToDegrees := ModR(0.0174532925199433 * degrees, pi*2); + + + + + ed9aeab3-29c3-4d5f-9dc8-19fc9c76a64e + + + + + + + + + + + + + + + Input value + + + + + + + + Input minimum + + + + + + + + Input maximum + + + + + + + + Output minimum + + + + + + + + Output maximum + + + + + Linear scaling of floating point number + + + + + IF inputLow = inputHigh THEN + ScaleReal := outputLow; +ELSE + ScaleReal := (outputHigh - outputLow) / (inputHigh - inputLow) * (LIMIT(inputLow, input, inputHigh) - inputLow) + outputLow; +END_IF; + + + + + f4fd53a7-9957-4944-8a08-2f548df9fcb7 + + + + + + + + + + + + + + + Input string + + + + + + + + Character to count + + + + + + + + + + + + + + Character pointer + + + + + + + + String length + + + + + + + + Current string position for iteration + + + + + Counts how many specified characters are contained in the string + + + + + charPointer := ADR(inputString); +length := LEN(inputString); +CountChar := 0; +FOR position := 1 TO length DO + IF charPointer^ = charToCount THEN + CountChar := CountChar + 1; + END_IF; + charPointer := charPointer + 1; +END_FOR; + + + + + 18cffb02-7390-4f3a-8476-2dd880b25886 + + + + + + + + + + + + + + + Input string + + + + + + + + What to remove + + + + + + + + + + For temporary string + + + + + + + + Position of found character + + + + + Remove character from string + + + + + tempString := inputString; +REPEAT + position := FIND(tempString, substringToRemove); + IF position <> 0 THEN + tempString := DELETE(tempString, LEN(substringToRemove), position); + END_IF; +UNTIL (position = 0) +END_REPEAT; +RemoveSubString := tempString; + + + + + e7c3cd13-9368-413f-9843-50ef6ed48f53 + + + + + + + + + + + + + + + Input string + + + + + + + + The setup.EXTENDED_ASCII value can be either TRUE or FALSE, depending on the character table supported by the PLC. Usually set to TRUE. + + + + + Change character case from lowercase to uppercase. Works only with ASCII Latin letters. + + + + + IF input > 96 AND input < 123 THEN + ToUpper := input AND 16#DF; +ELSIF input > 223 AND input <> 247 AND input <> 255 AND useExtendedASCII THEN + ToUpper := input AND 16#DF; +ELSE + ToUpper := input; +END_IF; + + + + + de50b872-7179-478a-a52a-b958ef69123e + + + + + + + + + + + + + + + Input date + + + + + + + + + + Year + + + + + + + + Month + + + + + + + + Day + + + + + + + + + + + + Extract year, month and day from DATE + + + + + // Get date as a string like 'D#2000-01-01' +sTemp := DATE_TO_STRING(input); +year := STRING_TO_UINT(MID(sTemp, 3, 4)); +month := STRING_TO_UINT(MID(sTemp, 8, 2)); +day := STRING_TO_UINT(MID(sTemp, 11, 2)); + +DateToElements := true; + + + + + b5fd857c-57e3-429c-980c-20e9ef873113 + + + + + + + + + + + + + + + Input date + + + + + + + + + + + + Determine day of week + + + + + NumOfDay := DATE_TO_DWORD(input) / 86400; +NumOfDay := NumOfDay + 3; +DayOfWeek := DWORD_TO_UINT(NumOfDay MOD 7); + + + + + 0f2fb405-5be6-4edf-872f-e9cea8ac3b5e + + + + + + + + + + + + + + + Input date in DATE format + + + + + + + + + + + + + + + + + + + + + + Calculate ordinal day of the year + + + + + DateToElements(input := input, year => year, month => month, day => day); + +DayOfYear := DaysTillMonth(year, month) + day; + + + + + 6eb00276-4ffa-4774-80ef-fed385777b26 + + + + + + + + + + + + + + + Year + + + + + Calculate number of days in year + + + + + IF IsLeapYear(year) THEN + DaysInYear := 366; +ELSE + DaysInYear := 365; +END_IF; + + + + + 8f928eba-c868-493d-b5ff-8dfedd52641d + + + + + + + + + + + + + + + Year + + + + + + + + Month + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Function to determine the number of days from the beginning of the year to the specified month. + + + + + DaysTillMonth := days[month] + BOOL_TO_UINT(month > 2 AND IsLeapYear(year)); + + + + + 67d1a212-1ccc-457e-b16d-c7e461443b5b + + + + + + + + + + + + + + + Year + + + + + Calculates how many days have passed until the specified year + + + + + // We get the number of years that have passed since 1970 +year := year - 1970; +DaysTillYear := year * 365 + ((year + 1) / 4) - ((year + 69) / 100) + ((year + 369) / 400); + + + + + d2e816a0-7963-4686-bc41-d296bea22188 + + + + + + + + + + + + + + Hours + + + + + + + + Minutes + + + + + + + + Seconds + + + + + + + + Milliseconds + + + + + Pack hours, minutes, seconds and milliseconds into TIME + + + + + HmsToTime := DWORD_TO_TIME(((h * 60 + m) * 60 + sec) * 1000 + ms); + + + + + 49706012-99ad-4259-bff9-b7c36e2caa74 + + + + + + + + + + + + + + Hours (0..23) + + + + + Convert hours (as INT) to TIME + + + + + HourToTime := T#1H * hours; + + + + + 93ab5816-722a-4fe0-b453-14d79d5f96e7 + + + + + + + + + + + + + + + Hours (0..23) + + + + + Convert number of hours (INT) to TOD (time of day) value. + + + + + HourToTod := TOD#00:00:00 + T#1h * hours; + + + + + ddb9ff52-023f-4749-8919-0e0538454a46 + + + + + + + + + + + + + + + Year + + + + + Determine if it's a leap year + + + + + IsLeapYear := year MOD 400 = 0 OR year MOD 4 = 0 AND year MOD 100 <> 0; + + + + + 5dbd663d-5e78-4699-b583-409a46d62852 + + + + + + + + + + + + + + + Longitude + + + + + + + + Date in UTC + + + + + + + + + + + + + + + + + Solar noon time at the specified longitude at the given time + + + + + t := UINT_TO_REAL(DayOfYear(dateInUtc)); +offset := -0.1752 * SIN(0.033430 * T + 0.5474) - 0.1340 * SIN(0.018234 * T - 0.1939); +SunMidday := HourToTod(REAL_TO_INT(12.0 - offset - longitude * 0.0666666666666)); + + + + + 68fe546b-72e8-4b0d-8ac1-742a18fb08fa + + + + + + + + + + + + + + Input time + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Extract hours, minutes, seconds and milliseconds from TIME + + + + + // Now the time is a string like 'TOD#12:12:59' +sTemp := TOD_TO_STRING(TIME_TO_TOD(input)); +h := STRING_TO_UINT(MID(sTemp, 5, 2)); +m := STRING_TO_UINT(MID(sTemp, 8, 2)); +sec := STRING_TO_UINT(MID(sTemp, 11, 2)); +TimeToElements := TRUE; + + + + + 3fe5a2e5-d274-4bd2-9993-e23e460bb606 + + + + + + + + + + + + + + + Current time + + + + + + + + Lower bound + + + + + + + + Upper bound + + + + + Function to determine if the current time is between two TIME_OF_DAY timestamps. + + + + + TodBetween := ( + FromTime > ToTime AND (CurrTime > FromTime OR CurrTime < ToTime) + ) OR (FromTime < ToTime AND (CurrTime < ToTime AND CurrTime > FromTime) +); + + + + + + b1ee0c3e-f6b4-4799-81ae-1ae2e0d1a89a + + + + + + + + + + + + + + Lower bound + + + + + + + + Upped bound + + + + + Calculate the difference between timestamps + + + + + IF ToTime < FromTime THEN + TodDiff := T#24H - TOD_TO_TIME(FromTime) + TOD_TO_TIME(ToTime); +ELSE + TodDiff := ToTime - FromTime; +END_IF + + + + + 50bec7af-ee3d-4afd-9afc-57186a003144 + + + + + + + + + + + + + + + Current date + + + + + Calculate the week number in the year + + + + + WeekOfYear := ((DayOfYear(currentDate) + 6) / 7); +IF DayOfWeek(currentDate) < DayOfWeek(YearStarts(currentDate)) THEN + WeekOfYear := WeekOfYear + 1; +END_IF; + + + + + 67cc401c-9be4-42bc-a783-173878e795d0 + + + + + + + + + + + + + + + Current date + + + + + + + + + + Temporary string + + + + + Calculate which day of the week the year starts on + + + + + str := DATE_TO_STRING(currentDate); +YearStarts := STRING_TO_DATE(CONCAT(CONCAT('D#', MID(str, 3, 4)), '-01-01')); + + + + + f574b5de-a869-4215-82cd-296f58bbc331 + + + + + + +
+ + + + + + + + Year + + + + + + + + Month + + + + + + + + Day + + + + + + + + Hour + + + + + + + + Minutes + + + + + + + + Seconds + + + + + + + + Milliseconds + + + + + + + + + + + + Pack year, month, day, hours, minutes, seconds and milliseconds into DT + + + + + sTemp := CONCAT('DT#', UINT_TO_STRING(y)); +sTemp := CONCAT(sTemp, '-'); +sTemp := CONCAT(sTemp, UINT_TO_STRING(mn)); +sTemp := CONCAT(sTemp, '-'); +sTemp := CONCAT(sTemp, UINT_TO_STRING(d)); +sTemp := CONCAT(sTemp, '-'); +sTemp := CONCAT(sTemp, UINT_TO_STRING(h)); +sTemp := CONCAT(sTemp, '-'); +sTemp := CONCAT(sTemp, UINT_TO_STRING(m)); +sTemp := CONCAT(sTemp, '-'); +sTemp := CONCAT(sTemp, UINT_TO_STRING(sec)); +sTemp := CONCAT(sTemp, '-'); +sTemp := CONCAT(sTemp, UINT_TO_STRING(ms)); +YmdhmsTodt := STRING_TO_DT(sTemp); + + + + + e464deaa-a738-4c18-a017-c8f42f407d70 + + + + + + + + + + + + + + + Year + + + + + + + + Month + + + + + + + + Day + + + + + + + + + + + + Convert year, month and day to DATE format. + + + + + sTemp := CONCAT('D#', UINT_TO_STRING(y)); +sTemp := CONCAT(sTemp, '-'); +sTemp := CONCAT(sTemp, UINT_TO_STRING(m)); +sTemp := CONCAT(sTemp, '-'); +sTemp := CONCAT(sTemp, UINT_TO_STRING(d)); +YmdToDate := STRING_TO_DATE(sTemp); + + + + + 9484e7aa-ff92-4a2c-99de-34c83c939af8 + + + + + + + + + + + + + + + Input number + + + + + Counts the number of bits equal to TRUE in DWORD. + Example: bit_count(3) = 2 Because 2 bits are 1, and the rest are 0. + + + + + WHILE input > 0 DO + IF input.0 THEN + BitCount := BitCount + 1; + END_IF + input := SHR(input, 1); +END_WHILE; + + + + + 26a551da-bc5b-40c3-a89e-69f572829323 + + + + + + + + + + + + + + + Byte to perform the operation on + + + + + + + + Value to assign + + + + + + + + Which bit number + + + + + + + + + + + + + + + Loads one bit value into a byte. + bit_load_b(2#0000_0000, 1, 2) = 2#0000_0010 + + + + + IF value THEN + BitLoadB := input OR SHL(dat, position); +ELSE + BitLoadB := input AND (NOT SHL(dat, position)); +END_IF; + + + + + 1e2df803-f917-4f56-b377-8bdb29e7bcae + + + + + + + + + + + + + + + Input number + + + + + + + + Bit number + + + + + Calculate the value of a single bit in a DWORD variable. + + + + + BitOfDword := (SHR(input, n) AND 16#00000001) > 0; + + + + + 858995be-7211-4c94-bcd7-bfdd7b773597 + + + + + + + + + + + + + + + Input value + + + + + + + + Bit number to invert + + + + + Invert the specified bit in DWORD + + + + + BitToggleDword := SHL(DWORD#1, position) XOR input; + + + + + 7f6f260e-6f32-4728-9857-76f270e6e081 + + + + + + + + + + + + + + + Input value X + + + + + + + + Input value Y + + + + + + + + Number of digits to compare + + + + + + + + + + + + Compares 2 input numeric variables to check if their first digits match. + Number of digits to compare is passed in parameter N. + cmp(3.141516, 3.141517, 6) will return TRUE. + + + + + tmp := ABS(x); +IF tmp > 0.0 THEN + tmp := Exp10(DINT_TO_REAL(FLOOR(LOG(tmp))-n+1)); +ELSE + tmp := Exp10(tmp); +END_IF; + +CompareReals := ABS(x - y) < tmp; + + + + + aa970595-45f5-4899-b174-d076f52d2dc1 + + + + + + + + + + + + + + + Input value + + + + + Truncates a floating-point number to a DINT integer. 1.5 becomes 1, and -1.5 becomes -1. + This function is necessary because REAL_TO_DINT may return different results on different systems. + + + + + DTrunc := REAL_TO_DINT(input); + +IF input > 0.0 THEN + IF DINT_TO_REAL(DTrunc) > input THEN + DTrunc := DTrunc - 1; + END_IF; +ELSE + IF DINT_TO_REAL(DTrunc) < input THEN + DTrunc := DTrunc + 1; + END_IF; +END_IF; + + + + + 2e55f2c0-fb39-4d0c-8d80-c5fb77a9a432 + + + + + + + + + + + + + + + Input value + + + + + Determines if a number is even or not. Returns TRUE if even, FALSE if odd. + + + + + Even := NOT input.0; + + + + + 9dc50582-69c8-4fd8-8f3b-d2bd8f2816fc + + + + + + + + + + + + + + + Previous value + + + + + + + + + + + + + + + + + + + + + + + + + Euler's constant + + + + + Calculates a pseudo-random number. To generate a number, the function + reads the PLC timer and creates a floating-point number from 0 to 1. To + use this function more than once in a single PLC cycle, you need to + call it with different values of the input parameter last. It's easier to use + the previous number to avoid repetitions. + + + + + tn := TIME_TO_DWORD(TIME()); +tc := BitCount(tn); +tn.31 := tn.2; +tn.30 := tn.5; +tn.29 := tn.4; +tn.28 := tn.1; +tn.27 := tn.0; +tn.26 := tn.7; +tn.25 := tn.6; +tn.24 := tn.3; +tn := ROL(tn, BitCount(tn)) OR 16#80000001; +tn := tn MOD 71474513 + INT_TO_DWORD(tc + 77); +Random := FRACT(DWORD_TO_REAL(tn) / 10000000.0 * +(E - LIMIT(0.0, last, 1.0)) +); + + + + + 7cb56466-a164-4a81-b192-885e8bbef47b + + + + + + + + + + + + + + + Input value + + + + + Swap bytes in DWORD + + + + + SwapDwordBytes := (ROR(input, 8) AND 16#FF00FF00) OR (ROL(input, 8) AND 16#00FF00FF); + + + + + bccdf6cd-e09b-4efd-9f7e-4630addfad75 + + + + + + + + + + + + + + + Input value + + + + + Swap bytes in WORD + + + + + SwapWordBytes := ROL(input, 8); + + + + + 4ffee399-140a-4885-98aa-fde9c899cd67 + + + + + + + + + + + + + + + 8c1463b5-02f2-427d-bd06-9e39d42fcbff + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 194758c..3ed71f6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,73 @@ -# codebox -Comprehensive PLC Utility Library for CoDeSys +# AnjLab Code Box - PLC Utility Library for CoDeSys + +## Overview + +AnjLab Code Box is a comprehensive PLC utility library designed for use with CoDeSys. It provides a collection of function blocks and functions that simplify common PLC programming tasks, including signal processing, time calculations, mathematical operations, string manipulation, and more. + +## Features + +### Signal Processing +- **Debounce Functions**: Filter noisy digital signals (`DebunceBoolIn`, `DebunceBoolInOut`, `DebunceBoolOut`) +- **Edge Detection**: Detect rising and falling edges with filtering (`DetectEdgeFiltered`) +- **Click Detection**: Detect double clicks and long presses (`DoubleClick`, `LongClick`) +- **PWM Generation**: Create PWM signals with configurable frequency and duty cycle (`PwmClock`, `PwmDc`, `PwmPw`) +- **Filtering**: Noise reduction filters for analog signals (`FilterNR`, `FilterTW`) + +### Time and Date Utilities +- **Sun Position**: Calculate sunrise, sunset, and solar noon times (`SunPositionTimes`, `SunMidday`) +- **Date/Time Conversion**: Extensive functions for date and time manipulation (`DateToElements`, `DayOfWeek`, `DayOfYear`, etc.) +- **Time Calculations**: Difference between timestamps, week of year calculations, etc. + +### Mathematical Functions +- **Rounding**: Ceil, floor, round, and truncate operations +- **Exponents**: Efficient power calculations (`ExpN`, `Exp10`) +- **Scaling**: Linear scaling of values (`ScaleReal`) +- **Random Numbers**: Pseudo-random number generation (`Random`) + +### String Manipulation +- **Character Operations**: Count characters, remove substrings, case conversion +- **String Processing**: Various string utility functions + +### Data Conversion +- **Bit Manipulation**: Count bits, toggle bits, load bits into bytes +- **Byte Swapping**: Swap bytes in words and dwords +- **IP Address Decoding**: Convert IP string to DWORD format + +## Installation + +1. Download the `.library` file from the releases section +2. Import the library into your CoDeSys project +3. Add the library to your application's dependencies + +Alternatively, you can import any POU exported by the library directly into your project from the source PLCopen XML file. + +## Usage + +After installation, you can call any of the functions or function blocks from your PLC code. For example: + +```iecst +// Example: Debounce a digital input +VAR + fbDebounce: DebunceBoolIn; + bFilteredOutput: BOOL; +END_VAR + +fbDebounce(input := %IX0.0, output => bFilteredOutput); +``` + +## Requirements + +- CoDeSys development environment (version compatible with PLCopen XML version 6.0.200) +- Target PLC that supports the standard function set used by the library + +## License + +Apache 2.0 + +## Contributing + +Contributions are welcome! Please fork the repository and submit pull requests with your improvements. + +## Support + +For support or feature requests, please open an issue on the GitHub repository.