Author Topic: Creating a Delay before initiating another plugin script  (Read 389 times)

spud1

  • Full Member
  • ***
  • Posts: 39
    • View Profile
Creating a Delay before initiating another plugin script
« on: December 22, 2018, 02:09:47 PM »
I would be very grateful for some help please.  I'm trying to create a delay before initiating a plugin, but am struggling to get the syntax right.  I've looked at various scripts on the forum, including timer.nut, but have been unsuccessful in adapting any of them.

I have 3 plugins as follows.  The first plugin called "Favourites.nut" initiates a series of bash scripts in the background.  This plugin also initiates the second plugin called "Delay.nut".  I'd like "Delay.nut" to wait 10 seconds, then call upon the third plugin.  The third plugin is called "RestartFavourites.nut" which switches to my Favourites layout.

The code I have so far is as follows.  First plugin - Favourites.nut:

Code: [Select]
// This plugin regenerates the "Favourites.txt" romlist when a game is selected as a favourite or deselected as a favourite
fe.add_transition_callback( "favourites" );
function favourites( ttype, var, ttime )
{
 switch ( ttype )
 {
  case Transition.ChangedTag:
                system( "sudo /bin/bash /opt/retropie/configs/all/favourites.sh" ); // Starts the Favourites bash script to update the Favourites romlist whenever a game is tagged or untagged
fe.do_nut("Delay.nut");
        return false;
}}

Second plugin - Delay.nut:

Code: [Select]
// This plugin delays switching back to the Favourites layout
fe.add_ticks_callback( "delay" );
function delay( ttime )
{
fe.do_nut("RestartFavourites.nut");
        return false;
}}

Third plugin - RestartFavourites.nut:

Code: [Select]
//Restart Favourites layout
function load_display_name(name) {
   foreach( idx, display in fe.displays )
      if ( name == fe.displays[idx].name && name != fe.displays[fe.list.display_index].name ) fe.set_display(idx)
}

load_display_name("Favourites")

Both the first and third plugins work perfectly without Delay.nut, but Delay.nut does not.  Could I please ask for some help to fix Delay.nut so that it waits 10 seconds before triggering RestartFavourites.nut?  Thanks.

I'm familiar with the sleep command for bash scripts and with AutoHotkey scripts for Windows, but I'm using a Raspberry Pi 3B so need to create the delay in Squirrel.
« Last Edit: December 22, 2018, 02:46:31 PM by spud1 »

keilmillerjr

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 759
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #1 on: December 22, 2018, 05:16:50 PM »
On the iPhone, so I’ll give tips. Out of state visiting family.

Get rid of delay.nut.

Favorites.nut: create a class that creates a ticks function and your transition. Ticks will update a class variable. When your transition case is met, make another class variable equal to your ticks. Return true until your ticks variable is greater than your target. Or however you do the math. It should work. Take a look at some of my plugins.

spud1

  • Full Member
  • ***
  • Posts: 39
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #2 on: December 24, 2018, 04:15:45 PM »
Thanks, keilmillerjr.  And Happy Christmas!

I'm definitely not expecting any response over the Christmas period.  Have a great time with your family.

I had a go at playing around with the squirrel scripts but I'm definitely not very good.  The principal one I looked at was this one:  http://forum.attractmode.org/index.php?topic=908.0

I adapted that script but have probably made a complete mess of it as it definitely doesn't work when enabled:

Code: [Select]
//Restart Favourites layout

class RestartFavourites {
active = false;
time =0;
delay = null;
starttime =0;

constructor( path, sec ) {
delay = sec.tointeger()*1000;


fe.add_transition_callback( this, "load_display_name" );
fe.add_ticks_callback( this, "Start" );
}

function Timer( ttime ) {
time = ttime;
}

function load_display_name(name) {
foreach( idx, display in fe.displays )
      if ( name == fe.displays[idx].name && name != fe.displays[fe.list.display_index].name ) fe.set_display(idx):
starttime = time + delay;
active = true;
break;
}
}

function Start( ttime ) {
if ( active && (ttime >= starttime ) )
active = false;
}
}

local start_layout_audio = RestartFavourites( "10" );

load_display_name("Favourites")

Thanks for your help (after the Christmas break).

spud1

  • Full Member
  • ***
  • Posts: 39
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #3 on: December 25, 2018, 02:22:24 PM »
I have tried to adapt one other script - qqplayer's script from here:  http://forum.attractmode.org/index.php?topic=2201.0

Code: [Select]
class UserConfig { </ label="Idle Timer ", help="The amount of time (in seconds) that if timer expired, go back to home menu", order=5 />  rtime=10;}
local my_config = fe.get_config();
local bgtime;
bgtime=abs(("0"+my_config["rtime"]).tointeger());
local  preview_counter =  fe.add_text( "", 1220 , 24 , 100, 25 );
//preview_counter.align = Align.Left;
//preview_counter.set_rgb( 255, 30, 30 );
//preview_counter.font = "archivonarrow-bolditalic";
local i=0;
local count = bgtime;
local user_interval = bgtime;
preview_counter.msg = count;
local previous_select = i;
local last_time = 0;
function saver_tick( stime )
{if ( previous_select != i) {
last_time = stime;
count = user_interval;
previous_select = i;
return;}
if ( stime - last_time > 1000) {
count--;
preview_counter.msg = count;
last_time = stime;}
if ( count <= 0 ) {
//Restart Favourites layout
function load_display_name(name) {
   foreach( idx, display in fe.displays )
      if ( name == fe.displays[idx].name && name != fe.displays[fe.list.display_index].name ) fe.set_display(idx)
}
}


load_display_name("Favourites")

fe.add_ticks_callback( "saver_tick" );
fe.add_transition_callback( "load_display_name" );

This did not work.

keilmillerjr

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 759
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #4 on: December 25, 2018, 03:32:56 PM »
I got you tonight bro. Traveling home. Kids will be asleep abs can get an hour in. You will have to test but I’ll give you a start.

spud1

  • Full Member
  • ***
  • Posts: 39
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #5 on: December 25, 2018, 03:54:41 PM »
Thanks, mate.  Have a safe drive.

keilmillerjr

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 759
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #6 on: December 25, 2018, 05:27:19 PM »
Try this. Remember it’s untested. You need to create a plugin folder. Good practice would be to create the bash script in the plugin folder. You can see looking at the script how I have it set up. Let me know if it works, and if it needs any tweaks. It should work with any Linux setup now, and could easily work with Mac OS X. I have no idea about windows. Zero idea. More familiar with dos.

Code: [Select]

// Plugin User Options
class UserConfig </ help="Plugin fades the screen when transitioning to and from a game." /> {
</ label="Wait time",
help="The amount of time in milliseconds to wait before switching to favorites display.",
order=1 />
wait="10000";
  </ label="Favorites Display Name",
    help="The name of your display name.",
    order=2 />
  displayName="Favorites";
}

// Auto Favorites
class AutoFavorites {
  user_config = null;
  wait = null;
  displayName = null;

  constructor() {
    user_config = fe.get_config();
    wait = user_config["wait"].tointeger();
    displayName = user_config["displayName"];

    fe.add_transition_callback(this, "transitions");
  }

  function transitions(ttype, var, ttime) {
    if ((ttype == Transition.ChangedTag) && (var == Info.Favourite)) {
      switch (OS) {
        case "Linux":
          system("sudo /bin/bash " + fe.module_dir + "update.sh");
          if (ttime >= wait) {
            loadDisplay(displayName);
            return false;
          }
          else {
            return true;
          }
          break;
      }
    }
    return false;
  }

  function loadDisplay(name) {
    foreach(idx, display in fe.displays)
      if ((name == fe.displays[idx].name) && (name != fe.displays[fe.list.display_index].name)) fe.set_display(idx);
    }
  }
}
fe.plugin["AutoFavorites"] <- AutoFavorites();

« Last Edit: December 25, 2018, 05:35:24 PM by keilmillerjr »

spud1

  • Full Member
  • ***
  • Posts: 39
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #7 on: December 26, 2018, 05:43:22 AM »
Thanks so much for your help on this.  I haven't been able to get the code to work so far.  Because I'm on a Raspberry Pi, I have a bit of trouble debugging scripts.

This is what I have done so far.  I created a sub-folder called AutoFavorites in the plugins folder.  In that subfolder, I've put a plugin.nut with all the code.  The only change I made was as follows: displayName="Favourites"; because I'm using the English spelling.

In the AutoFavorites subfolder, I've put an executable bash script called "update.sh", the contents of which are:
Code: [Select]
#!/bin/bash
sudo nohup /opt/retropie/configs/all/favourites1.sh &

ie the bash script calls another bash script in the background.

I just wanted to check with you that the AutoFavorites subfolder will be picked up by "fe.module_dir".

The only other thing I noted was that when I enabled the plugin there was only the name of the plugin and the line to enable/disable it.  Wasn't sure whether there should be anything else.

Thanks so much for your help on this.  It is really appreciated, especially over the Christmas break.

keilmillerjr

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 759
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #8 on: December 26, 2018, 08:57:56 AM »
Duplicate a case for windows or Mac, and leave out the system call to make it easier to debug. Print to console. What’s your second bash script?

spud1

  • Full Member
  • ***
  • Posts: 39
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #9 on: December 26, 2018, 02:17:28 PM »
Hi.  Sorry, I'm not sure I understand.  I'm a bit obtuse!  Do you mean install Attract Mode on Windows and use the exe for console?  Or change the plugin so the case is "Windows" rather than "Linux", remove the line beginning system(" etc?

The "favourites.sh" calls a series of bash scripts that are in my /opt/retropie/configs/all folder.  I can confirm that these bash scripts all work to regenerate my Favourites romlist automatically in the background after being invoked by favourites.sh.

Here they are:

The plugin:
Code: [Select]
// This plugin regenerates the "Favourites.txt" romlist when a game is selected as a favourite or deselected as a favourite
fe.add_transition_callback( "favourites" );
function favourites( ttype, var, ttime )
{
 switch ( ttype )
 {
  case Transition.ChangedTag:
                system( "sudo /bin/bash /opt/retropie/configs/all/favourites.sh" ); // Starts the Favourites bash script to update the Favourites romlist whenever a game is tagged or untagged
        return false;
}}

"favourites.sh":
Code: [Select]
#!/bin/bash
sudo nohup /opt/retropie/configs/all/favourites1.sh &

"favourites1.sh":
Code: [Select]
#!/usr/bin/env bash
# Adds a semi-colon at the end of each line of a tag file, so that it decreases the possibility of every game called "1942" (for example) being added to Favourites (eg "1942;" is more limiting than "1942").
sleep 7
cd /home/pi/.attract/romlists
for i in "*.tag"; do
    sed -i 's/$/;/' $i
done
sudo nohup /opt/retropie/configs/all/favourites2.sh &

favourites2.sh:
Code: [Select]
#!/usr/bin/env bash
# The grep command is limited to Fixed Strings eg "1942;".
# The original command for grep was:   cat "${romlist}.txt"|grep -F -w "${gamename}" >> Favorites.txt
# But the "whole word" option -w would not pick up any game with a semi-colon at the end eg it would pick up "1942" but would not pick up "1942;"
# The next command I used was: cat "${romlist}.txt"|grep -F "${gamename}" >> Favorites.txt.  With the semi-colon added, this would ensure that the name of the game was treated as a Fixed String.  It would not capture every single 1942, only those ending with the semi-colon.  However, not only would it capture "mario", it would capture "drmario" ie words with other letters at the front.  In order to avoid this, I had to use the following:
# cat "${romlist}.txt"|grep "\<${gamename}" >> Favorites.txt.  Together with placing the semi-colon at the end, the \< meant that it would ignore games where there were letters at the beginning eg drmario.
# The Favorities.tag file is renamed so that it is not included in the creation of the new Favorites romlist.
cd /home/pi/.attract/romlists
# rm -f Favorites.tag
mv -f Favorites.tag Favorites.tag.backup
mv -f Favorites.txt Favorites.txt.backup
ls *.tag > tagfiles
while read filename
do
  while read gamename
  do
    romlist=`echo ${filename} |cut -f 1 -d '.'`
    cat "${romlist}.txt"|grep "\<${gamename}" >> Favorites.txt
  done < "${filename}"
done < tagfiles
rm -f tagfiles
sort Favorites.txt > Favorites1.txt
rm -f Favorites.txt
mv -f Favorites1.txt Favorites.txt
sort Favorites.txt | uniq >> Favorites1.txt
rm -f Favorites.txt
mv -f Favorites1.txt Favorites.txt
sudo nohup /opt/retropie/configs/all/favourites3.sh &

favourites3.sh:
Code: [Select]
#!/usr/bin/env bash
# Removes the trailing semi-colon at the end of all tag files so the tag files go back to "normal".
cd /home/pi/.attract/romlists
for i in "*.tag"; do
    sed -i 's/;*$//g' $i
done
sudo nohup /opt/retropie/configs/all/favourites4.sh &

favourites4.sh:
Code: [Select]
#!/usr/bin/env bash
# Changes ownership of Favorites.txt to pi and makes it read and write compatible by anyone.
sudo chown pi:pi /home/pi/attract/romlists/Favorites.txt
sudo chmod 0777 /home/pi/attract/romlists/Favorites.txt
sudo nohup /opt/retropie/configs/all/favourites5.sh &

favourites5.sh:
Code: [Select]
#!/usr/bin/env bash
# Copies the first field (ie the name of each game) from the new Favorites.txt and creates a Favorites.tag with it and ensures that only one copy of the game appears in the tags.
# Changes ownership of Favorites.tag to pi and makes it read and write compatible by anyone.
cd /home/pi/.attract/romlists
# cut -f 1 -d '.' Favorites.txt > Favorites.tag
sudo awk -F';' '{print $1}' Favorites.txt > Favorites.tag
sort Favorites.tag | uniq >> Favorites1.tag
rm -f Favorites.tag
mv -f Favorites1.tag Favorites.tag
sudo chown pi:pi /home/pi/attract/romlists/Favorites.tag
sudo chmod 0777 /home/pi/attract/romlists/Favorites.tag
# sudo nohup /opt/retropie/configs/all/favourites6.sh

Also, I'm just wondering whether your debug plugin could be used to send output to a file rather than to the console.  I'm using ssh from my Windows PC so it would pretty easy to check what the output error was if errors were redirected to a file.
« Last Edit: December 26, 2018, 02:23:16 PM by spud1 »

keilmillerjr

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 759
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #10 on: December 26, 2018, 02:59:49 PM »
[shift + page down] or [Shift + page up] will scroll through the console in linux. You can debug right from your pi. Just run the plugin and then exit attract to the terminal. If using ssh, the terminal output for attract mode can be viewed directly from your ssh session!

Or you can troubleshoot right from mac or windows, everything except your bash scripts. Add another operating system to the plugin I wrote. If you use linux on your desktop, you can fully test! For this reason, I will recommend again you bundle the scripts with the plugin. Those bash scripts should be combined into a single file. Performance is important to narrow down the stall.

spud1

  • Full Member
  • ***
  • Posts: 39
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #11 on: December 28, 2018, 02:39:06 PM »
Thanks for this.  Sorry, I haven't been able to get back to this because of real life, but should be able to do so in the next day or so.  Thanks for all your help and patience.

keilmillerjr

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 759
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #12 on: December 28, 2018, 05:25:37 PM »
Thanks for this.  Sorry, I haven't been able to get back to this because of real life, but should be able to do so in the next day or so.  Thanks for all your help and patience.

I’m the worst with this since being married and having two kids. I miss every timeline now. No worries.

spud1

  • Full Member
  • ***
  • Posts: 39
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #13 on: December 31, 2018, 04:14:36 PM »
Hi.  Thanks for your patience.  I'm finally getting back to this. 

I did what you suggested - combined all of the bash scripts into one script.  However, although it works, now there is a pause in the front end.  When I press "Yes" to add a game to the favourites romlist, the front end pauses for about 10 seconds.  So it appears that the bash scripts don't work in the background, whereas if I string them together using the "nohup" (background) option, they work properly in the background.

My configuration is more or less silent so what I've done now is change the autostart.sh to "attract --loglevel debug".  That is now giving me enough information.  I'll report back in the next post what information I get.


spud1

  • Full Member
  • ***
  • Posts: 39
    • View Profile
Re: Creating a Delay before initiating another plugin script
« Reply #14 on: December 31, 2018, 04:29:50 PM »
The response I get is:

Code: [Select]
/home/pi/.attract/plugins/AutoFavorites/plugin.nut line = (50) column = (2) : error expression expected
Script Error in /home/pi/.attract/plugins/AutoFavorites/plugin.nut - expression expected

The whole script is:

Code: [Select]
// Plugin User Options
class UserConfig </ help="Plugin fades the screen when transitioning to and from a game." /> {
</ label="Wait time",
help="The amount of time in milliseconds to wait before switching to favorites display.",
order=1 />
wait="10000";
  </ label="Favorites Display Name",
    help="The name of your display name.",
    order=2 />
  displayName="Favourites";
}

// Auto Favorites
class AutoFavorites {
  user_config = null;
  wait = null;
  displayName = null;

  constructor() {
    user_config = fe.get_config();
    wait = user_config["wait"].tointeger();
    displayName = user_config["displayName"];

    fe.add_transition_callback(this, "transitions");
  }

  function transitions(ttype, var, ttime) {
    if ((ttype == Transition.ChangedTag) && (var == Info.Favourite)) {
      switch (OS) {
        case "Linux":
          system("sudo /bin/bash " + fe.module_dir + "update.sh");
          if (ttime >= wait) {
            loadDisplay(displayName);
            return false;
          }
          else {
            return true;
          }
          break;
      }
    }
    return false;
  }

  function loadDisplay(name) {
    foreach(idx, display in fe.displays)
      if ((name == fe.displays[idx].name) && (name != fe.displays[fe.list.display_index].name)) fe.set_display(idx);
    }
  }
}
fe.plugin["AutoFavorites"] <- AutoFavorites();

Line 50 is just the "}" on the second last line.

I was also wondering whether there might be a way to call the plugin from a bash script.  I've looked online but can't find anything.