Task needs another pair of eyes.

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
Post Reply
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Task needs another pair of eyes.

Post by Don_Kirby »

This task has been running for about 5000 hours. In the past, I have noticed glitches here and there, but never had any record of it beside the occasional "what was that?".

In order to locate the problem, I set up a datalogger on the comport to monitor the timing information. After 860 Hours and 14 minutes, the time stopped moving for a number of hours, then started again. The rest of the application, including this task, was still running (the com routine is embedded in it). I'm at a loss as to what the problem is, especially after running flawlessly for so long. The effect is as if the WaitForInterval never triggers, although, if that were the case, the console output routine wouldn't run either.

The code is shown below. Coding conventions aside, are there any hidden problems here that I'm not seeing?

Code: Select all

Public Sub HourTask() 'Hour Meter Task - Call this task in Sub Main() to start counting
	Dim I As Byte
	Dim Loc as Byte
	Dim Temp as Single
	Dim SECONDS_VALUE as Byte
	Temp = 0.0
	Loc = 0
	SECONDS_VALUE = 0
	For I = 0 to 199
		If Hours(I) > Temp Then 
			Temp = Hours(I)
			Loc = I
		End If
	Next

	Call SetInterval(1.0) 'set the interval
	Do
		Call WaitForInterval(0) 'Wait for 1 Second
		SECONDS_VALUE = SECONDS_VALUE + 1
		If SECONDS_VALUE >= 60 Then
			Hours(Loc) = Temp + 1.0 'increment the value
			Temp = Hours(Loc)	
			'Circular Buffer
			Loc = Loc + 1
			If Loc >= 200 Then
				Loc = 0
			End If
			SECONDS_VALUE = 0
		End If
		
		'*********Monitor Output******************************
		If Monitor > 0 Then 'logger output (once per second)
			Console.WriteLine(ConvertTime(HoursTotal()) & "," & C & "," _
			& CStr(CBool(Register.PortC>=&hF0)) & "," & CStr(Alarm_Request))
		End If

	Loop
End Sub
-Don
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Task needs another pair of eyes.

Post by dkinzer »

Don_Kirby wrote:This task has been running for about 5000 hours. In the past, I have noticed glitches here and there,
This type of problem is difficult due to the elapsed time issue. If you aren't already doing so, you may want to log the data to a file that is sent by the ZX. That way, you'll have an historical record of what transpired. If necessary, you can build a half-Y serial cable that allows the transmitted data to be sent both to a PC and another destination.

Other ideas: you may want to run only the HoursTask to see if the problem occurs in the absence of any other activity. Also, you may want to temporarily re-write the HoursTask so that it uses no strings or string functions at all. You'll need to write some conversion routines that produce characters that can be sent directly to the Com1 output queue.

Code: Select all

Dim b as Byte
' compute a character to write
b = someValue
Call PutQueue(CByteArray(Register.TxQueue), b, 1)
- Don Kinzer
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Re: Task needs another pair of eyes.

Post by mikep »

Don_Kirby wrote:In order to locate the problem, I set up a datalogger on the comport to monitor the timing information. After 860 Hours and 14 minutes, the time stopped moving for a number of hours, then started again.
This is a hard one. Is the problem repeatable - let's us know in another 35 days :)

You could try setting the interval to 1 instead of 1.0. The code would run 512 times quicker so you may see a problem after only an hour or two.
Mike Perks
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Post by Don_Kirby »

I'm logging the output via a serial port logging application, so there is a record of the issue. I wouldn't have known about it otherwise, except for the occasional 'glitch'. With that in mind, a wise embedded programmer once said "There are no glitches, only unfound bugs."

At the expense of the EEPROM, I'm going to speed up the routine so I don't have to wait another 35 days... I'm curious if I can reproduce the issue.

As per Don's suggestion, I'll have to run 2 tests. One to see if the task is the problem, and one to see if intertask 'weirdness' is the issue.

-Don
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Post by Don_Kirby »

Well, so far I haven't been able to reproduce the bug.

Normally, the task calls WaitForInterval in order to loop just once per second. For this test, I've removed the call entirely, but still cannot get the loop to speed up by more than a factor of ten.

The problem area is a function that cycles through the EEPROM array of 200 singles to find the largest value. Rather than looping through all of them, can anyone suggest a faster method?

The current code is shown below.

Code: Select all

Public Function HoursTotal() As Single 
'call this function to retrieve the current value in minutes.
	Dim I As Byte
	HoursTotal = 0.0
	For I = 0 to 199
		If Hours(I) > HoursTotal Then
			HoursTotal = Hours(I)
		End If	
	Next
End Function
I'm trying to wrap my brain around a method to find the highest value without the loop. When the values are written, they are in consecutive order. The modification shown below speeds up the loop a bit.

Code: Select all

Public Function HoursTotal() As Single 
'call this function to retrieve the current value in minutes. 
	Dim I As Byte
	HoursTotal = 0.0
	For I = 0 to 199
		If Hours(I) > HoursTotal Then
			HoursTotal = Hours(I)
		ElseIf Hours&#40;I&#41; < HoursTotal Then
			Exit For
		End If	
	Next
End Function

This loops faster, but the loop time varies with how many array locations have to be checked. Is there a 'loopless' method?

-Don
twesthoff
Posts: 247
Joined: 17 March 2006, 6:45 AM
Location: Fredericksburg, VA

Task needs another pair of eyes.

Post by twesthoff »

Don_Kirby wrote:can anyone suggest a faster method
The easy thing is don't use a Single variable. Since you are counting seconds, use a Long. It will be a lot faster, or an Integer if that is big enough...
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Don_Kirby wrote:Rather than looping through all of them, can anyone suggest a faster method?
Add another Persistent variable that holds the largest value seen so far.

You'll probably want to go through the list at startup and set the "largest so far" variable but after that, just update it (if the data warrants) whenever a new data item is added to the array. Depending on what else you're doing with the data you might be able to abandon the 200 item array altogether.

One thing to note is that the VM routines that write Persistent data read the location first before they write. If the location already contains the correct data, a write is not performed. Although this strategy costs a few CPU cycles, it minimizes "wear" on the EEPROM cells.
- Don Kinzer
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Post by Don_Kirby »

The reason for the array is to spread the write cycles out over many cells, hopefully to extend the EEPROM life. If I constantly write to one cell to remember the highest value, that one cell will wear out long before the array does. On the other hand, if I write the high value to a RAM variable, I can keep the array (for saving the information across power cycles) and have quick access to the value while the application is running.

-Don
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Post by Don_Kirby »

Well, it seems that I already had the code in place to do it. The variable 'Temp' shown in the first post holds the most recent value. The only change I needed to make pertained to the possibility of the task not running when the HoursTotal function is called. To account for that, a TaskIsValid check is performed, and if false, the loop method is used.

I've also changed the Singles to Longs, although they are both 4 bytes, so I don't think it will make a difference speed-wise.

-Don

Edit: without the loop delay, and running without the WaitForInterval, the clock has accumulated 8 hours in about 10 minutes. That should speed up the testing dramatically.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Don_Kirby wrote:they are both 4 bytes, so I don't think it will make a difference speed-wise.
Operations (other than loading/storing) on Single values take longer than operations on Long values. A Single comparison takes roughly 37uS while a Long comparison takes about 20uS.
- Don Kinzer
stevech
Posts: 715
Joined: 22 February 2006, 20:56 PM

Post by stevech »

I have an application that logs certain data to peristent storage. The technique I use is probably widely used, and is, in gist:

All data items are in a well defined structure that is created and maintained in RAM. A from-to region in EEPROM is set aside for a data log which contains one or more such structures.

Each structure has an "ascencion number". It begins at 1 for an empty log.
At power-up/reset, the data log region is searched for the largest ascencion number. This number is remembered in RAM. Note: the region-scan is done only once, at reset.

To add another data log entry, I use the saved highest-ascencion number to compute the offset into the EEPROM. Now I increment the ascencion number then copy the in-RAM data structure along with the new ascencion number, to EEPROM.

This has worked well on a 24/7 application in ZX24.

Some details:
must modulo-region-size to compute EEPROM offset; this permits overwriting the oldest log data.
Post Reply