Previous Thread
Next Thread
Print Thread
Rate This Thread
Hop To
Page 1 of 2 1 2
#4508325 - 02/24/20 07:49 PM Start a timer on a key press...  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
I want to monitor the time of a certain event, let's say my engine boost preset to a limit of 5 min for example...

I want to press a key to "on" a flag and start counting...then if I press that key I "off" the flag, and it stops counting, if I press it again, it resumes the counting and then beep when time is over.

This is beyond my programming knowledge, so please can someone help me implementing this?

Thanks in advance.

Inline advert (2nd and 3rd post)

#4508336 - 02/24/20 10:08 PM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
You could use REXEC to repeat counting up a variable every second. You can start and stop the REXEC using a key, but if you don't clear the time variable, it will continue to count up to 5 minutes or what ever you want.

Code
int timevar = 0;
MapKey(&Joystick, S1, REXEC(REXEC_Handle, 1000, "timevar = timevar + 1;", RNOSTOP);     // Start counting
MapKey(&Joystick, S2, EXEC("StopAutoRepeat(REXEC_Handle);");                  // Stop Counting
MapKey(&Joystick, S3, EXEC("timevar = 0;");                                   // Clear time variable


Make sure to define REXEC_Handle as a number (e.g. define REXEC_Handle 1) or just replace REXEC_Handle with any number not previously used in an REXEC.

The 1000 in REXEC is 1 second. So the timevar = timevar + 1 will execute every 1 second. Change this to count however fast or slow you want. An REXEC every second should cause no performance issues. It might be a problem if you want it to count every 100ms or faster.

The RNOSTOP allows the REXEC to continue running even after S1 has been released. Without it, the REXEC only executes while you are pressing the S1 button. The manual explains all this pretty well if you haven't used REXEC before.

Note, you can clear the count anytime without having to stop and restart the REXEC. REXEC doesn't know about or care about the value of timevar.

#4508377 - 02/25/20 08:59 AM Re: Start a timer on a key press... [Re: Drakoz]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
Thank you Drakoz, I will try that.

But now thinking better about it, I don't want to start the counter by pressing a key (my fault on the first post).

Rather I want the timer to start counting cumulative time "every time" an axis is above a certain value (like throttle being above 90% for instance).

So the timer variable has to keep records of the time that axis is above a certain value, and then give the alert when it reaches the maximum time allowed (let's say 5 min).

I think it will have to be something like

check if T16000[THR] > Threshold, then REXEC(…) to start counting
then if T16000[THR] < Threshold, then EXEC("StopAutoRepeat…
and if timer > 300 then fire some beep or speak some text to give the alert

This would be more like it... to create a function I guess... please advise.
Thanks a lot.

#4508383 - 02/25/20 10:54 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
You could use KeyAxis() commands to call REXEC in the upper 10% and StopAutoRepeat in the lower 90% (e.g. using a LIST(0,90,100) with the AXMAP2 command).

Or if you want to do something non-standard, you can always put if/else statements in EventHandle() to do the checks and call the REXEC/StopAutoRepeat functions. But, I don't think that is necessary for what you are describing.

#4508412 - 02/25/20 03:46 PM Re: Start a timer on a key press... [Re: Drakoz]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
Originally Posted by Drakoz
You could use KeyAxis() commands to call REXEC in the upper 10% and StopAutoRepeat in the lower 90% (e.g. using a LIST(0,90,100) with the AXMAP2 command).

I think I should be ok with this solution, let me try it out...

#4508499 - 02/26/20 01:17 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
This actually works...
Code
include "target.tmh"

int time = 0;

int main()
{
    if(Init(&EventHandle)) return 1;

	KeyAxis(&T16000, THR, 0, 
		AXMAP2( 
			LIST( 0, 10, 100 ), 
			REXEC(1, 1000, "time = time + 1; if (time == 10) printf(\"time elapsed \\xa\");", RNOSTOP),
			EXEC("StopAutoRepeat(1);")
			)
		);
}

int EventHandle(int type, alias o, int x)
{
    DefaultMapping(&o, x);
}

In the example above I used 10 seconds as a test. And the List (0,10,100) is because my THR is inverted.

But in reality I need something a little more complex... What I want is to monitor is the virtual axis, not the physical one.

This is what I need to check: Axis[DX_SLIDER_AXIS].pos > axisvalue(90) , assuming I want to check if the virtual axis value is above 90%

I believe the way out is the EventHandle...

#4508614 - 02/27/20 08:42 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
Maybe it is just my low skills on C programming, but I can't get REXEC/StopAutoRepeat to work inside EventHandle()…

And just a question: if I want to print out the variable time as in my code above, for monitoring purposes, what is the correct printf syntax to do this?

Thank you.

#4508722 - 02/28/20 02:44 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Re: how to use printf() to print out a variable, I started a new thread on that here: https://SimHQ.com/forum/ubbthreads.php/topics/4508687

Now your real question...

Actually the detail you missed isn't a C programming thing, but a TARGET thing. EXEC and REXEC can only be executed inside another function like MapKey(), KeyAxis(), or in this case, you needed to use ActKey(KEYON+REXEC() ). smile2

Below is a highly over blown example showing 3 different timers. This example should answer a lot of questions you may have about making timers, using code in EventHandle(), and printing information with printf(). I used the Warthog Left Throttle axis (&Throttle and THR_LEFT), so you'll need to global find and replace to change for your T.16000M (change &Throttle to &T16000, and THR_LEFT to THR).

Timer 1 (time1) runs due to a KeyAxis. This is your original example slightly updated. It monitors the real axis on the Warthog. Timer 1 runs any time the Warthog THR_LEFT axis is > 90%. The KeyAxis() command starts, stops, and resets REXEC #1 all on it's own (no code in EventHandle() ).

Timer 2 (time2) runs due to the DX_ZROT_AXIS DirectX axis > 90%. This is what you wanted to create. Timer 2 is incremented every second by RXEC #2. It is started and stopped using code in EventHandle(). I use REXEC #3 to report and reset timer 2. REXEC #3 is started in the main() section using an ActKey(). REXEC #3 is free running. It checks the value of timer 2 every 0.1 seconds and if time2 > 10 seconds, the REXEC will print a message and reset the timer.

Timer 3 (time3) is just free running. Every 10 seconds, a message is printed for timer 3, and timer 3 is reset. This timer is entirely managed using an ActKey() command in main().

Any time you move the real axis, the real axis, DirectX axis, and the value of all 3 timers is reported in the TARGET console.

For the Warthog, by default, 100% is when the Throttle axis is pushed all the way forward, and 0% is pulled all the way back. But in my example, I assigned the Warthog Throttle THR_LEFT axis to the DirectX axis, DX_ZROT_AXIS, and I did NOT reverse the axis. Hence when the THR_LEFT axis is at 100%, the DX_ZROT_AXIS axis is at 0%. I did this on purpose so timer 1 would demonstrate counting up when the THR_LEFT axis is forward, and timer 2 would demonstrate counting when the THR_LEFT Axis is all the way back. Of course adjust these as needed for your device.

Code
include "target.tmh"

define	AMAXRANGE	(AMAX+AMAX+1)				// AMAX=32767, AMAXRANGE=65535, AMAX from defines.tmh
int time1 = 0;          // time real axis spends above 90%
int time2 = 0;          // time DirectX axis spends above 90%
int time3 = 0;          // timer to run freely regardless of axis or DXaxis values
int time2active = 0;    // flag to know if timer 2 is running or not. active = 1, stopped = 0

int main()
{
    if(Init(&EventHandle)) return 1;

    MapAxis(&Throttle, THR_LEFT, DX_ZROT_AXIS, AXIS_NORMAL, MAP_ABSOLUTE);

    // time1 - activated by the real axis position >= 90% and managed by the KeyAxis() command below.
    //  A note about EXEC or REXEC... Note below how I put multiple commands for the REXEC on different lines.
    //  This makes it more readable vs. putting the entire set of commands on a single line.  If you do this, 
    //  notice how every line has to be enclosed in it's own quotes.  This is because you cannot have a new line
    //  inside the quotes for an EXEC or REXEC.  This is often a point of confusion witih EXEC or REXEC.  
    //  Alternatively, create a separate function call and have the EXEC or REXEC call the function call.  
	KeyAxis(&Throttle, THR_LEFT, 0, 
		AXMAP2(
			LIST( 0, 90, 100 ), 
            EXEC("StopAutoRepeat(1);"),
			REXEC(1, 1000,
                "time1 = time1 + 1;"
                "if (time1 > 10) {"
                "   printf(\"time1 elapsed \\xa\");"
                "   time1 = 0;"
                "}"
                , RNOSTOP
            )
		)
	);

    // time2 - activated by the DirectX axis position >= 90% and managed with code in EventHandle()
    //  The following checks timer 2 every 100ms and resets it if > 10 seconds. 
    //  So if you want to take some action (press a key, reset a variable), you would use the ActKey()
    //  command below to do this even though timer 2 is started and stopped in EventHandle().  
    //  This is because EventHandle() is only called when you press a button or move an axis, but the REXEC()
    //  below gets executed every 100ms.  
    ActKey(KEYON+
        REXEC(3, 100,       // check timer 2 every 100 ms
            "if (time2 > 10) {"
            "   printf(\"time2 elapsed \\xa\");"
            "   time2 = 0;"
            "}"
            , RNOSTOP
        )
    );

    // time3 - A free running 10 second timer
    ActKey(KEYON+
        REXEC(4, 1000,
            "time3 = time3 + 1;"
            "if (time3 > 10) {"
            "   printf(\"time3 elapsed \\xa\");"
            "   time3 = 0;"
            "}"
            , RNOSTOP
        )
    );
}

int EventHandle(int type, alias o, int x)
{
    DefaultMapping(&o, x);

    int position;
    float percent;

    // if Warthog Thorttle Left axis moves, print some info and take some action
    if ((&o == &Throttle) & (x == THR_LEFT)) {

        // time1
        // Print time1 timer value
        position = o[THR_LEFT];
    	percent = 100.0*(position+AMAX)/AMAXRANGE;      // convert axis -32767 to +32767 value to %
        printf("Axis position = %0.1f%% (%d), \x9Time1 = %d  \x9\x9", percent, position, time1);

        // time2
        // Update time2 timer due to DirectX Axis DX_ZROT_AXIS >= 90%. Print time2 timer.  
        position = Axis[DX_ZROT_AXIS].pos;
    	percent = 100.0*(position+AMAX)/AMAXRANGE;      // convert axis -32767 to +32767 value to %
        printf("DXaxis position = %0.1f%% (%d), \x9Time2 = %d  \x9\x9", percent, position, time2);
        // If DirectX Axis DX_ZROT_AXIS > 90%, start time2 timer.
        if (percent >= 90) {
            if (!time2active) {     // do this only if time2 is NOT active already
                ActKey(KEYON+REXEC(2, 1000, "time2 = time2 + 1;" , RNOSTOP) );
                time2active = 1;    // set flag to say time2 is active
            }
        }
        // otherwise, stop time2 timer if DX_ZROT_AXIS < 90%
        else {
            StopAutoRepeat(2);      // Note, StopAutoRepeat() does NOT need an ActKey() to call it. 
            time2active = 0;
        }

        // time3
        // Print time3 timer value.
        printf("Time3 = %d\xa", time3);
    }
}

#4508754 - 02/28/20 10:25 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
Thanks a lot... it would take me sometime before I figure out I have to use ActKey(KEYON+REXEC())…
I also didn't know about AMAX... I created a function myself to handle this the 65534 range conversion to % and vice versa, not an issue though.

Anyway, with the printf now out of the way, and these superb examples, things look pretty straight forward.
Thank you again for your time explaining all this.

#4508874 - 02/29/20 04:10 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Even I was confused about the ActKey() thing for a moment.

Ya, AMAX is defined in defines.tmh. I created AMAXRANGE in my .ttm file as:

Code
define	AMAXRANGE 	(AMAX+AMAX+1)				// AMAX=32767, AMAXRANGE=65535, AMAX from defines.tmh


In target.tmh, they always just say (AMAX+AMAX+1) when they need this number. I got tired of typing that.

#4508888 - 02/29/20 10:35 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
The timer 2 on the code above is really what I wanted.

I had to update this line
Code
if ((&o == &T16000) & (x == THR) | (&o == &T16000) & (x == TS2) ) (…)
because I have power settings in the TS2 button, and otherwise the EventHandle() was only recognizing axis movements not key presses.Didn't know if this was the correct way to do it, but it did solved the problem.

Since the time this timer is counting is 3min, 5min, etc. depending on the plane, I would like to add one minute callouts...
Can you help me with a formula for this?

#4508988 - 03/01/20 09:06 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
If it works, it was the correct way to do it. smile2

There is a precedence for operators like ==, &, and | in C. For example & has higher precedence over |. So precedence issues are the only thing that might have messed you up. I sometimes forget the exact precedence, but if in doubt, look it up, or enclose in more () to make sure it goes the way you want. In TARGET, since these scripts are often read by people that aren't programmers, I tend to use more () to make it absolutely clear.

#4508989 - 03/01/20 09:12 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Oh, missed your last question....

Not sure what you mean by help with the formula for 1 min. call outs. The timer examples I made count seconds (1000 ms for each REXEC). So make a beep sound or text to speech every 60 seconds instead of the 10 second examples I gave. Is that what you mean?

#4509000 - 03/01/20 01:22 PM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
What I mean is...

Up to now, I load only one script to handle the planes I usually fly, now with the help of the text to speech implementation everything I need is annunciated.
For the "war emergency power" (wep) our example of 10 seconds is now 5min, sometimes 3min, depending on the aircraft profile.

As at now, every time my throttle exceeds a certain value (is different from aircraft to aircraft) the timer counts it, and just one minute before the total wep available it announces "one minute" before it reaches the total time preset where it announces "time elapsed". This is already very good, as I can manage the wep and keep that last minute for a last emergency.

What I was trying to add is a "by minute annunciation" kinda like " four minutes remaining","three minutes remaining", "two minutes remaining", "one minute remaining", "wep time elapsed".
This would provide even a better engine management.

I have a variable named wep which states the time available for each aircraft engine.
I have a variable named percent (from your code) which sets the threshold of my DX_SLIDER_AXIS also for loaded with each profile.
And I have your time2 variable which counts the time above the threshold.

I know I can add if conditions for every case but it's going to be a lot of ifs... I was thinking about a simple math formula to cope with it. That's it.
But I'm very happy with what was achieved so far. Very helpful indeed.

#4509129 - 03/02/20 09:51 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Ah, got it. Here is a suggested way to do it. It is arguable if this is an easier or better solution than just a bunch of if statements with the exact text you want. Also, I'm not happy with this as overall it is more complicated then I would like, but it was just a first pass example of one way you might want to do it. If I was going to use this in a lot of scripts, I might think of a cleaner better way to do it, but it doesn't really matter. It works and doesn't represent any noticeable performance hit.

You might want to glance at my code first, then read my poorly written explanation below, and then ask questions if anything doesn't make sense.

There are three global variables I have defined at the top of the file:

int WEPtime = 5*60; // seconds - maximum time allowed for engine to operate in WEP (in seconds) - decrement this
int WEPTimerActive = 0; // flag to know if WEP Timer is running or not. active = 1, stopped = 0
int WEPTimerElapsed = 0; // flag to indicate WEP has elapsed (timer ran down to 0)

You need to define these globally (the int <variablename>; must be outside of any functions like in my example). Also, inside your profile change routine, every time you change profiles, you must set these variables for the new aircraft. So WEPtime is 5 min. for one aircraft, and 3 min for another for example. Both WEPTimerActive and WEPTimerElapsed must be set to 0 when you change to a new aircraft. WEPTimerActive is like before, if the timer is counting, this =1. If not counting, this =0. The new one is WEPTimerElapsed, which when =1, means the WEP time has run out and we have spoken "WEP time elapsed". After speaking that term, I assume you don't need any more warnings, so the timer is permanently stopped until you change to a new profile and reset the variables above again. This also means you will want some method to reset these variables for the current airplane profile for when you get a new aircraft but didn't change profiles. To replicate this, I mapped the Warthog Throttle APENG button to reset to 5 minutes again. Also, you have to move the DX Axis < 90% before you reset the timer, or the timer will start counting the moment you touch the axis.

I re-wrote my Timer 2 routine in a new function called WEPTimerStartStop(). This is called in EventHandle(). WEPTimerStartStop(), like before, looks for the DirectX Axis to be >= 90%, and it will start or stop the timer as needed. Unlike before, this new function starts the timer using an REXEC() that calls a new function, CheckWEPTime(), and CheckWEPTime() deals with all the specifics.

CheckWEPTime() both decrements the counter from WEPtime down to 0 seconds (yes, the timer decrements now instead of counting up), and it monitors the count so that every minute, it calls SpeakWEPTime() to speak an update message.

So all of that was just re-writing my Timer 2 example from before so that it works better with what SpeakWEPTime() does. This helps us because now every minute, we can say SpeakWEPTime(x) and SpeakWEPTime() will speak "x minutes remaining". Or if x=0, then it speaks "wep time elapsed".

Lastly, I copied in my Speak() function which I demonstrated in a different forum topic of course.

Here is the code.

Code
include "target.tmh"

define	AMAXRANGE	(AMAX+AMAX+1)		// AMAX=32767, AMAXRANGE=65535, AMAX from defines.tmh

// The following global variables must be initialized every time you load an airplane profile.
int WEPtime = 5*60;         // seconds - maximum time allowed for engine to operate in WEP (in seconds) - decrement this
int WEPTimerActive = 0;     // flag to know if WEP Timer is running or not. active = 1, stopped = 0
int WEPTimerElapsed = 0;    // flag to indicate WEP has elapsed (timer ran down to 0)

int main()
{
    if(Init(&EventHandle)) return 1;

    // Map axis to be monitored
    MapAxis(&Throttle, THR_LEFT, DX_ZROT_AXIS, AXIS_NORMAL, MAP_ABSOLUTE);

    // Reset WEP Timer (like a profile change, or to reset when you get a new airplane on same profile)
    //  Make sure you move the axis to < 90% range, or the timer will start immediately once you touch
    //  the axis.  
    MapKey(&Throttle, APENG, EXEC("ResetWEPTimer();") );

}

//******************************************************************
int EventHandle(int type, alias o, int x)
{
    DefaultMapping(&o, x);
    WEPTimerStartStop(&o, x);
}



//******************************************************************
//*************  Extra Functions Below  ****************************
//******************************************************************

//******************************************************************
// WEPTimerStartStop()
//  Starts and stops the WEPTime Timer based on DirectX Axis DX_ZROT_AXIS >= 90%.
//  Calls CheckWEPTime() to decrement WEPTime and Speak minute countdown.  
int WEPTimerStartStop(alias o, int x)
{
    if (WEPTimerElapsed) return 1;  // If already given Elapsed message, exit, nothing to do anymore.

    int position;
    float percent;
    // if Warthog Thorttle Left axis moves, print some info and take some action
    if ((&o == &Throttle) & (x == THR_LEFT)) {
        // WEPTime Timer
        // Update WEPTime timer due to DirectX Axis DX_ZROT_AXIS >= 90%. Print time2 timer.  
        position = Axis[DX_ZROT_AXIS].pos;
    	percent = 100.0*(position+AMAX)/AMAXRANGE;      // convert axis -32767 to +32767 value to %
        printf("DXaxis position = %0.1f%% (%d), \x9Time2 = %d \xa", percent, position, WEPtime);
        // If DirectX Axis DX_ZROT_AXIS > 90%, start decrementing WEPTime timer.
        if (percent >= 90) {
            if (!WEPTimerActive) {     // do this only if WEPTime timer is NOT active already
                ActKey(KEYON+REXEC(1, 1000, "CheckWEPTime();", RNOSTOP) );
                WEPTimerActive = 1;    // set flag to say WEPTime timer is active
            }
        }
        // otherwise, stop WEPTime timer if DX_ZROT_AXIS < 90%
        else {
            StopAutoRepeat(1);      // Note, StopAutoRepeat() does NOT need an ActKey() to call it. 
            WEPTimerActive = 0;
        }
    }
}

//******************************************************************
// ResetWEPTimer()
//  Resest the timer parameters due to a profile change or reset in the same aircraft.
int ResetWEPTimer() {
        printf("WEP Timer Reset\xa");
        StopAutoRepeat(1);          // Stop WEP Timer
        WEPtime = 5*60;         // Set to 5 minutes
        WEPTimerActive = 0;     // Set Timer to not active
        WEPTimerElapsed = 0;    // Reset Timer to not elapsed
}


//******************************************************************
// CheckWEPTime()
//  Decrements WEPTime every second.
//  Checks value of WEPTime timer, speaks a WEP count down every minute, and stops timer when 
//  Max WEP time is elapsed.  
int CheckWEPTime()
{
    if (WEPTimerElapsed) return 1;  // If already given Elapsed message, exit, nothing to do anymore.
    WEPtime = WEPtime - 1;      // Decrement by 1 second

    int min = WEPtime/60;       // quotient
    int sec = WEPtime%60;       // remainder
    printf("Time:  Min=%d, Sec=%d \xa", min, sec);
    if (sec == 0) {                 // if remainder is 0...
        SpeakWEPTime(min);          // ...speak minute message
        if (min == 0) {             // if quotient = 0, we have run WEP out (elapsed) so...
            StopAutoRepeat(1);      // ...stop timer
            WEPTimerActive = 0;
            WEPTimerElapsed = 1;    // ...WEP timer is elapsed and no need to count anymore
        }
    }
}


//******************************************************************
// SpeakWEPTime()
//  Speak the number of minutes remaining for WEP.  Max 5 minutes (increase the MinCount string array for more).
//  Usage:
//      min = number of minutes remaining
//      SpeakWEPTime(5);   Will speak "five minutes remaining"
//      SpeakWEPTime(0);   Will speak "WEP time elapsed"
//
// An array of words where &MinCount[0] = "one", &MinCount[4] = "five"
alias sp1 = "one";  //  These must be defined globally (outside a function) because the quoted text are constants.
alias sp2 = "two";
alias sp3 = "three";
alias sp4 = "four";
alias sp5 = "five";
int MinCount[] = {&sp1, &sp2, &sp3, &sp4, &sp5};    // MinCount[] is an array of addesses to the above constants.
int SpeakWEPTime(int min)
{
    alias spMinCount;
    char buffer; Dim(&buffer, 32);  // make sure buffer size is large enough for your longest spoken sentence - 32 is plenty here

    if ((min < 0) | (min > 5)) return 0;    // min out of range, exit with 0 to indicate error

    if (min == 0)
        sprintf(&buffer, "wep time elapsed");
    else {
        &spMinCount = MinCount[min-1];          // Get the number word from MinCount[]
        if (min > 1)
            sprintf(&buffer, "%s minutes remaining", &spMinCount);
        else if (min > 0)
            sprintf(&buffer, "%s minute remaining", &spMinCount);
    }

    Speak(&buffer);     // Speak the warning
    return 1;           // word spoken, exit with 1
}


//******************************************************************
// Speak()
//	Text to Speech - this function uses Windows mshta.exe and a simple Visual Basic script to perform text to speech
//	text - the text or phrase to speak
//	This concept of using mshta vbscript was found here:  https://stackoverflow.com/questions/1040655/ms-speech-from-command-line
//	More info on mshta.exe which is a part of Internet Explorer intended to execute HTA files (HTML Applications).
//		https://en.wikipedia.org/wiki/HTML_Application
//	No special programs are required for this as it uses default Windows programs and Visual Basic Script (VBScript) for HTML.
//	If you have removed Internet Explorer from your computer, this may not work.  
int Speak(alias text)
{
    //if (!SpeakEnabled) return 0;		// disable TextToSpeech sounds
	if (&text == "") return 0;			// exit if no parameter given
    if (strlen(&text) > 167) {          // spoken sentence + spawn mshta line below cannot exceed 256 characters
        printf("Speak:  String is to long\xa");
    }

	printf("Speaking:  %s\xa", &text);
	char buffer; Dim(&buffer, 256);
    sprintf(&buffer, "spawn mshta vbscript:Execute(\"CreateObject(\"\"SAPI.SpVoice\"\").Speak(\"\"%s\"\")(window.close)\")", &text);
    system(&buffer);
}


I think you will understand most of what I did, but the SpeakWEPTime() function does some things that are not entirely obvious.

The point of SpeakWEPTime() was to create an array of text - five, four, three, two, one. Doing that in C is easy. Doing it in TARGET, which isn't quite C is a little odd. I had to create the 5 alias spx variables (e.g. sp1, sp2...). The MinCount[] is created as an array of the memory addresses of the spx variables (e.g. &sp1, &sp2, ....). This allows me to use sprintf() to build a sentence using MinCount[x-1] where x is the number of minutes remaining, and x-1 is because all arrays start at 0.

I think you understand the sprintf stuff from ohter posts - where we sprintf a sentence into buffer. Then we can just say Speak(buffer) and it say what we want.

Note, it is required that sp1, sp2, etc. and MinCount[] are defined globally (outside any functions). Otherwise, this doesn't work. In C, this wouldn't be required, but in TARGET it is because the "quoted text" like "one", "two", etc. are constants. You can't define a constant inside a function.

Sorry some of this might be confusing because it is a lot more C and programming like than TARGET like. But it is a great example to explain some details about how TARGET works with strings and arrays of strings. I hadn't tried this before and it took a little thinking about it to get it to work because TARGET doesn't handle strings like normal C.


Last edited by Drakoz; 03/02/20 10:07 AM. Reason: Fixed a bug in my code example, updated my description
#4509190 - 03/02/20 03:53 PM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
Everything makes sense in your explanation, but yes, it's even more complicated than I expected... rolleyes

Two things I didn't mention but you figured it out:
1) when you change profile you reset everything, then the new aircraft profile sets the new WEPtime variable
2) I have a button combination to reset the timer, in case of a respawn (I just landed, and I respawn to fly another sortie in the same aircraft)

In fact these two items above, I have them already implemented in my current code because they are essential.

Now let me take my time and apply this code and see how ti goes.
Thanks a lot.

PS: Just wondering if Target programming doesn't work exactly as C, why it is not documented... it seems to me some of the stuff discussed here had to be "figured out", or maybe Target was not meant to be used to do such complicated scripts...

#4509264 - 03/02/20 11:05 PM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
Just implemented in my script... works like a charm!!
Does everything I want it to do. Just perfect.
THANK YOU!!

#4509289 - 03/03/20 09:13 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Glad to hear it. I figured you might want to change it a little or simplify it, but if it works perfectly, then no need.

You originally asked for the "equation" or "method" to make this work, and I guess the key details were counting down from 5 minutes to 0 so the count would actually represent the actual minutes left. This makes it easier for several reasons. Plus using division of an integer (/), which returns the quotient, and modulo (%), which gives the remainder, hence we get X minutes as the quotient, and XX seconds for the remainder. When the remainder = 0, we know a minute has passed and it is time to announce that. Those were the key details at least.

#4509460 - 03/04/20 04:13 PM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: May 2001
Posts: 468
Joao Muas Offline
Member
Joao Muas  Offline
Member

Joined: May 2001
Posts: 468
Portugal
Even if one could sort out this or a similar algorithm to get this timer to work, the printf support is key for a proper testing and "calibration" of the thresholds.
So that clarification on how to handle the printf in Target came very handy.
After everything was tested and working as I wanted, I commented "//" all printf statements, leaving only the "spoken" ones, because I'm not looking at the Target console anymore.

I'm so pleased with this timer that I might even add a second one for the engine combat power mode, which is usually 15, 30, sometimes 60min depending on the aircraft, and triggered at lower throttle settings. I'm just hesitating because I don't want to end up with a mix of announcements happening altogether. But that would be another very helpful tool for the simulation, no doubt.
I think that with two timers "on", when the engine is set to WEP, it's better to stop the combat power announcements, but still keep counting. That might be the way to go.
Maybe I will give it a try...

Just another question: can this timer code be set on a different file and then called by the main script by an "include" statement, or similar"? It's a routine that can be used by other scripts, so I was just wondering...

#4509856 - 03/07/20 08:34 AM Re: Start a timer on a key press... [Re: Joao Muas]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Originally Posted by Joao Muas
Just another question: can this timer code be set on a different file and then called by the main script by an "include" statement, or similar"? It's a routine that can be used by other scripts, so I was just wondering...


Yes, all these functions like beep(), Speak(), my timer functions, etc. can all be copied into a separate file (call it a .tmh file, though the extension doesn't matter) and added to any of your scripts with an include at the top of the file.

The include statement takes the other file and effectively copies it into your main file at the location of the include statement. So in the end, TARGET sees a single "file" which includes all the included other files all in one and then compiles and runs it.

Page 1 of 2 1 2

Moderated by  RacerGT 

Quick Search
Recent Articles
Support SimHQ

If you shop on Amazon use this Amazon link to support SimHQ
.
Social


Recent Topics
Carnival Cruise Ship Fire....... Again
by F4UDash4. 03/26/24 05:58 PM
Baltimore Bridge Collapse
by F4UDash4. 03/26/24 05:51 PM
The Oldest WWII Veterans
by F4UDash4. 03/24/24 09:21 PM
They got fired after this.
by Wigean. 03/20/24 08:19 PM
Grown ups joke time
by NoFlyBoy. 03/18/24 10:34 PM
Anyone Heard from Nimits?
by F4UDash4. 03/18/24 10:01 PM
RIP Gemini/Apollo astronaut Tom Stafford
by semmern. 03/18/24 02:14 PM
10 years after 3/8/2014
by NoFlyBoy. 03/17/24 10:25 AM
Copyright 1997-2016, SimHQ Inc. All Rights Reserved.

Powered by UBB.threads™ PHP Forum Software 7.6.0