Frequency of crontab calling a php function - Looking for alternative options - php

I'm programming my own "smart home" as a learning project.
My code is running fine. I'm looking for help to improve the efficiency and of the code and/or the setup of crontab + php code.
I'm monitoring the energy consumption of my washing machine with a WIFI energy meter. Target is to notify me once the washing machine is completed so I don't forget to clear it.
on my Pi I have a crontab like so:
*/20 7-22 * * * /usr/bin/php '/home/holger/html/plugs/washer.php'
which runs following php code (I simplified for better readability):
[...]/I call the function, of course, but this function does the main task
function loop($maschine, $watt_init, $trashhold){
$max = 75;//max loops to avoid endless runs
$i = 1;//start counter
$tackt = 3;//tact time to check energy consumption
//$trashhold = 4;//ab x Watt kein standby
if ($watt_init < 1 ) {//Machine is switched off if energy consumption < 1 Watt
die;//quit
}
elseif ($watt_init < 2 ) {//Machine is switched off or in standby if energy consumption < 1 Watt
die;//quit
}
else {//Any thing else: Machine is running
while ($i < $max) {//loop as long as max loops are not reached
$watt_current = json_combine(IPplug5);//getting current energy consumption from WIFI energy meter via JSON
sleep(60*$tackt);//sleep and continue every 60s x tact time
$i++;//increase counter +1
//compare actual consumption with defined trashhold
if ($watt_current[0] >= $trashhold) {//continue while energy consumption bigger then trashhold
continue;//repeat loop
}
elseif ($watt_current[0] < $trashhold) {//stop if energy consumption lower then trashhold
break;//stop loop
}
}
echo "Program done. please clear. Runtime: " . $i*$tackt. "Min."
//[...] message me to my telegram bot
}
}
The code is running fine and I'm getting the output I need.
My question is: Is there a better way to do that?
Currently I'm afraid to overload my Pi with too many open php sessions, therefore I'm starting the code only every 20min and also let the while loop sleep for 3 Min. But for improved accuracy I like to run the cronjob more often and also let the while loop sleep only for 30s.
My requirements are to stick to my PI and php code and not to use any available software like Home Assisant.io as it contradicts with my learning approach.
Any ideas or insights welcome.

Ideally it's a not the best approach to handling and measuring power consumption. It would be best if you created an API that accepts events like on/off or threshold hold limit extends from your IP devices.  Further you can create logs and store them in databases.
Although, for your current problem here is one alternate solution.
Set your cron that runs every second.
function get_powerConsumption($machine, $watt_init, $threshold)
{
if ($watt_init < 2) {
exit();
}
$time = date("Y-m-d H:i");
$filename = $machine . '_power_consumption.log'; // expecting some machine identification name here. otherwise ignore prefix
$watt_current = json_combine(IPplug5);
if ($watt_current[0] >= $threshold) {
$data = array(
$time,
$watt_current[0]
);
file_put_contents($filename, json_encode($data) . "\n", FILE_APPEND);
} elseif ($watt_current[0] < $threshold) {
$data = array(
$time,
'stopped'
);
file_put_contents($filename, json_encode($data) . "\n", FILE_APPEND);
}
}
Create another cron to look up for stopped events logged in the file. if found process the calculation based on logged data like time and consumption. You can set this cron to run based on your need like every second or minute or after some interval.
Also, handle code to delete old logs, once stopped events found.

Related

Does PHP sleep() function allow to free the main thread for a specified amount of seconds?

I have a web application that allows the users to upload DBF files and the app will store contents into an SQL database. The row count range from a few thousands to about 80,000 rows and I have the following code
if($file){
$totalRows = dbase_numrecords($file);
for($i = 1; $i <= $totalRows; $i++){
$row = dbase_get_record_with_names($file, $i);
//echo $row["BILL_NO"]." ";
if(!empty(trim($row["STATUS"]))){ //save to database if column is not empty
$data = [
//array data from the row
];
$db->table("item_menu")->replace($data);
}
if($i%1000 == 0) //Sleep call here every 1000 rows
sleep(1);
}
echo "done";
}
This function, once done will be called once per day and ideally just called/run in the background. However, when I do not place the sleep function, the server doesn't serve any pages until the loop completes, which can take from a few seconds to about a minute of unresponsiveness, but when the sleep function is added, the server continuously serve pages to different users.
My question is, does the sleep function help free up the current thread and process other requests during the sleep period?
If you use sleep() function then you will end up executing all the thing in a single thread causing a pause on the whole process. You should go for php v8.1 for that kind of process handling.

php continue execution after leaving the page

I have a simple script that counts from 1 to 5000 with a for loop. It flushes output in real time to browser and shows a progress bar with %.
What I have: If I leave the page, the process interrupts. If I come back, it starts from 0.
What I want to achieve: If I leave the page, the process continues and, If I come back , it shows the right percentage.
Example: I run the process, it counts till 54, I leave the page for 10 seconds, when I come back it shows me 140 and continues to flush.
Is it possible?
I would suggest you to use server workers - scripts which are intended to run independently from webserver context.
The most common way of doing it - usage of message queues (RabbitMQ, Qless, etc). Event should be initiated by the script in web context, but the actual task should be executed by queue listener in a different context.
What you have asked seems quite simple to do with a session. (Purely assuming on the use case given). This is not running any process in the background, it just simply keep track of the time and show the progress. That's why I said "based on what you asked". If you want to keep track of any real background tasks, then I believe the case would be totally different, and you will have to change the wordings of your question as well ;)
Something like this would do.
<?php
session_start();
$s = &$_SESSION;
$sleep = 1; //seconds
//check if we have a value set in session before, if not set default = 0.
if(!isset($s['last'])){
$s['last'] = 0;
}
//check if we have a last time set in session before. if not set a default = curret time.
if(!isset($s['time'])){
$s['time'] = time();
}
//get the idle time of the user.
$idle = time() - $s['time'];
//start the loop..and set starting point.
$start = $s['last'] + ($idle / $sleep);
for( $i = $start; $i < 100; $i++){
echo $i . '<br />';
$s['last']++;
$s['time'] = time();
flush();
sleep($sleep);
}
Hope it helps!!

PHP countdown timer without causing a spinlock

The problem:
I'm a programming student and currently studying PHP. Apparently Java can designate threads for things like countdown timers; however, I have been told that PHP can have issues with a standard countdown function using delay, or even time() logic, as it will result in a spinlock. How do I go about avoiding a spinlock and having a simple, efficient countdown timer?
Summary of what I am trying to solve:
I need to create a 30 second timer on the backend PHP. Once the timer is completed, PHP will use my Slack API to communicate with a particular Slack channel - letting everyone know that the coffee is done, etc. With this said, I need to be able to start multiple clocks (as there is both coffee and tea) and must avoid a spin lock as it will kill the multi-countdown ability that is required.
Code so far that results in spinlock:
$startTime = time();
$actualTime = (int)$startTime;
$finishTime = ((int)$startTime) + 30;
var_dump($startTime);
var_dump($actualTime);
var_dump($finishTime);
while(($finishTime - $actualTime) > 0) {
usleep(250000);
$actualTime = ((int)time());
if ($actualTime === $finishTime) {
echo "Tea is ready!";
}
}

For Loop, Sleep, Page Not Loading due to Loop

I am creating a small Bitcoin payment gateway to pay 0.25 BTC and only need 1 confirmation. I have created a form (form.html) which shows a unique random address ($_POST['address']) so on the next page after you hit submit I need it to display a page and do some checks for me... but when I hit submit it just says "waiting for page to load" at the bottom of my browser and doesn't actually load the page up (which has HTML on it), I'm sure this is because of a for loop hogging the page but I'm not sure how to get around it.
My for loop
for ($i=0; $i <= 900; $i++) {
$conf = file_get_contents('https://blockchain.info/q/addressbalance/' . $_POST['address'] . '?confirmations=1');
$seen = file_get_contents('https://blockchain.info/q/addressfirstseen/' . $_POST['address']);
if ($seen != 0) {
if($conf >= 25000000) {
echo "Payment Complete <br><br>";
break;
} elseif ($conf != 0 && $conf < 25000000) {
echo "You Did Not Pay Enough Bitcoins<br><br>";
break;
}
}
sleep(30);
}
I'm pretty rusty with PHP and this is my first attempt in a while, if anyone could point me in the right direction to what I am doing incorrect that would be much appreciated.
Thank you for any time anyone spends on this.
you define i as a loop counter but you are not using it?!
most probably none of the break criteria is fulfilled and you are actually doing a big sleep 900 * 30 sec = 450 minutes
I'm absolutely sure that you don't need this.
not to mention that you have to change some vars in cnf, ini ... if you need a response after that long time
from what I can see, $seen is probably zero by default and/or $conf is < = 0
sleep
for delay in milliseconds, use
usleep
haven't used php for some years, but I believe sleep is in sec.
try not doing front-end checks with back-end language as the first comment under your post says but I can only guess what you are trying to achieve ...

Executing php function every x amount of time for multiple users

got this code:
<?php
function test ($url){
$starttime = microtime(true);
$valid = #fsockopen($url, 80, $errno, $errstr, 30);
$stoptime = microtime(true);
echo (round(($stoptime-$starttime)*1000)).' ms.';
if (!$valid) {
echo "Status - Failure";
} else {
echo "Status - Success";
}
}
test('google.com');
?>
I want to have an option to execute this function every 5mins / 1hour / 1 day etc.. I was suggested using cron, but i never heard of cron before and after doing some research i understood that its a sepperate file, that would exeute the function every x amout of time. What if i would have multiple users, for example userA would want to run the script every 5mins, and userB would want to run the script every hour. In this sittuation i would need to create multiple cron files for each user?
Edit:
I was thinking about doing something like this:
for ($i = 0; $i < 100; $i++) {
test('google.com');
sleep(10 * 60);
}
Only in the sleep line i would have a custom $n field that each user would define by themselves. My problem with this was - it returns results only when the full cycle has finished, i would want it to return result after every "round?" (idk what its called, but basically would give 1 value, then 10mins later 2nd value and so on) .
The only way I found is to use an ajax method which will be called from the user page within a setTimeout.
See : settimeout function
May be you need use cron (UNIX) or Windows Tasks (Microsoft) depending the OS on your server.
Cron
Windows Task
Greetings,

Categories