Class Data History Loading


burt4munger

Recommended Posts

At program start, I'm querying a log table in a SQL server database to get the last 4 days of history for some variables so the history graph will work. The data does not come in through a channel so is not persisted in the normal way. I run a SQL server query which returns all the data values in descending time stamp order. I ran into an issue loading those back into the array, which after further reading here appears to be a known bug with class variables and using [] and () in the same line. I've simplified the whole thing down to what breaks (when it issues an error: C1060 Improper number of parameters: Init Line 28

sequence classValve:

class Valve

local Position

endclass

sequence Init:

beginseq(classValve)

delay(2)

for (private.i = 0, i < 3, i++)

global Valve = new(Valve)

endfor

this segment throws an error and stops after the second AddValue call for index i = 0:

for (private.i = 0, i < 3, i++)

try

for (private.j = 0, j < 40, j++)

? doubletostr(i) + ":" + doubletostr(j)

Valve.Position.AddValue(InsertTime(j,systime()-40,-1))

endfor

catch()

system.ErrorMessage(strlasterror)

return

endcatch

endfor

This however works:

private z = 0

for (private.i = 0, i < 3, i++)

try

for (private.j = 0, j < 40, j++)

? doubletostr(i) + ":" + doubletostr(j)

z = Valve

//z.position.AddValue(InsertTime(j,systime()-j,-1)) //this way fails too, but for index i=1

//but it works like this

z.position[ j ] = j

z.position.time[ j ] = systime() - j

endfor

catch()

system.ErrorMessage(strlasterror)

return

endcatch

endfor

I lost a few hours tracking this down today, and was going to just post it here, but saw the Comm object discussion and realized that this seems to be an example of a known class parsing bug (if I'm doing something wrong tell me!). I just want to see if there is any other explanation, and to say that however undocumented, a major reason I continue to use DAQFactory is the querytoclass and class tools, without which my program would be a giant mess and much harder to write and maintain. So for the wish list I'd like to see the new update have these bugs solved!

ArrayTest.ctl

Link to comment
Share on other sites

What release of DAQFactory are you running? Because when I run the following code, it works flawlessly:


class Valve
local Position
endclass


//beginseq(classValve)
delay(2)
for (private.i = 0, i < 3, i++)
global Valve = new(Valve)
endfor


for (private.i = 0, i < 3, i++)
try
for (private.j = 0, j < 40, j++)
? doubletostr(i) + ":" + doubletostr(j)
Valve.Position.AddValue(InsertTime(j,systime()-40,-1))
endfor
catch()
system.ErrorMessage(strlasterror)
return
endcatch
endfor

? "next..."

private z = 0
for (private.i = 0, i < 3, i++)
try
for (private.j = 0, j < 40, j++)
? doubletostr(i) + ":" + doubletostr(j)
z = Valve
//z.position.AddValue(InsertTime(j,systime()-j,-1)) //this way fails too, but for index i=1

//but it works like this
z.position[ j ] = j
z.position.time[ j ] = systime() - j
endfor
catch()
system.ErrorMessage(strlasterror)
return
endcatch
endfor[/CODE]

Note also that your loop initializing Valve does nothing useful (the loop part). It just keeps reinstatiating Valve and assigning it to the same variable. Also, I don't think DF cares, but you really should use unique names in the global scope, including class names. In otherwords don't use a variable named "Valve" if your class is named "Valve" too. I typically follow Microsoft MFC convention and put "C"'s in from of my class names.

Hmm, now I see. The code you posted isn't the same as your .ctl doc. You removed the array indexing on Valve. Yes, that is correct, you can't put [] and () in the same line. The parser gets confused. You need to assign the indexed object (Valve[i]) to a private variable, then use that variable to avoid having the [i] in the same line as the ().

Link to comment
Share on other sites

yes, the part that broke used Valve.Position.AddValue(InsertTime(j,systime()-40,-1))

ok, I'll fix the class names to prefix with a c, that makes sense.

So far I've kept each class definition in its own sequence which was why the beginseq(classValve) in my code. Can more than one class be defined in a sequence? I think I started putting one per sequence because it would not work to do this in a single sequence

class cValve

local position

...function stuff..

endclass

class cTemp

local sensor

...functions...

endclass

.

So just doing global Valve = new(cValve) will allow me to start accessing Valve[n].position for any any value of n?

Is there any difference between loading data using the inserttime as I did above or just

for (i = 0, i < records, i++)

Valve = 32

Valve.Time = timestamp

Link to comment
Share on other sites

shoot, last post somehow went when I tried to tab the code.

anyway, my version = 5.87a build 1972

before my last post flew off, I was asking about the difference between inserttime function with a -1 increment compared to just explicitly defining data and time local variables for the class variable, and then setting them both from the data, which comes from a SQL query, ordered by DAQTime desc, so it should still build an array backwards in time, that I can resume adding new values to using the .addvalue(inserttime(x,systime(), -1)) function

Link to comment
Share on other sites

1) MFC actually uses a capital "C" for classes...

2) you can put as many class declarations into a single sequence as you want. You can put other code in their too. Only code outside class/endclass actually executes. The rest define the class(es). I typically put most of my class declarations in a couple sequences, or even one. It depends how many I have and how big they are.

3) doing "value.time = timestamp" is the same as doing "value = insertTime(value, timestamp,0)" The 0 at the end doesn't matter if value is a scalar. But really, insertTime is typically used to fill an array with uniform timestamps, or in cases where you want to insert time in the same line as something else (such as in an AddValue() statement). You can also assign arrays of time, so if you have two arrays, dbValue and dbTime that you are from two different fields of your database and they are the same size, you can do:

value = dbValue

value.time = dbTime

And it will make value an array with all your data with the appropriate time stamps. You may need to do sortTime() after that, or better yet, have your SQL query order it from largest to smallest time.

4) the issue with the [] and () in classes only applies at the top level, so doing:

objectArray[myfunc()].value

will work fine because the myfunc() is inside the [].

Link to comment
Share on other sites

So I understand #3 above for a single sensor, but for an array of those sensors there's something I am missing regarding the implicit time and initialization. It seems like it should be easy.

I want an array of sensor values with their times for 50 sensors, so that Temp[4] has all the datapoints and times for sensor 4

Once a minute I store their values in a SQL database, along with TimeStamp of systime()

At program startup I'm trying to load their historical values using this looped query (which doesn't seem to work):

for (sensorNo = 0, SensorNo < 50, SensorNo++)

strSQL = "Select ActualTempC, TimeStamp from Log where SensorNo = " + doubletostr(sensorno) + " and TimeStamp > " + doubletostr(fourdaysago) + " order by timestamp desc"

history = db.QueryToClass(sqldb, strSQL)

Temp[sensorNo] = history.ActualTempC

Temp[sensorNo].time = history.TimeStamp

endfor

if Temp[4].time[32] is time for the 33rd datapoint, how do I reference the value?

I'm trying to add new values each time the sensor is read by Temp[4].AddValue(InsertTime(21.2, systime(), -1))

I'm trying to graph the values by using Temp[4] as the data value, which seems to work when the values have all been added by AddValue during the sensor reads, but won't work after I try to reload them from the database.

should I set this up something like

Class Ctemp

local value

local time

endclass

global Temp = new(Ctemp)

or is global Temp = 0 sufficient because of the automatic time value and automatic array sizing?

then Temp[4].value[33] would be the temperature at sample 33 and Temp[4].time[33] would be the sample time?

I'm a little wrapped around the axle with the undeclared array sizes and values!

Link to comment
Share on other sites

Time is always in the first dimension of the array. Doing temp[4].time[32] actually isn't a valid statement, or at least won't give you what you think. So, you can't have an array of things in the first dimension that have history. Temp[4].addValue() for example is also not valid because addValue() wants to add another row to Temp. You can create multidimensional arrays, but time is always in the first dimension and would be the same time for every column (the second dimension). The best way to visualize it is to think of an Excel spreadsheet. If you do temp[0], temp[1], temp[2] you are referencing rows 1, 2 and 3. There can be one time stamp for each of these rows. You can have multiple columns: temp[0][0] is A1, temp[4][2] is D3, but temp[4][2] and temp[4][6], since they are in the same row, have to have the same time stamp.

To do what you want you need to have an array of objects. You don't need to split value and time (and also you can't name variables or channels either "Time" or "Value" as both are reserved words). So just:


class CTemp
local val
endclass

[/CODE]

Then you can instantiate an array of these objects:

[CODE]
global temp
for (private i = 0, i < 10, i++)
temp[i] = new(CTemp)
endfor

[/CODE]

And you can treat each temp independantly. You can assign values into the val of a temp with addValue(), or use the method I described for setting a whole array. Either way, you are likely to need to split the statements due to the bug we talked about at the beginning.

[CODE]
private t = temp[4]
t.val = history.ActualTempC
t.val.time = history.TimeStamp

[/CODE]

You may be able to get away with temp[4].val = history.actualTempC. I don't remember truthfully. It only has one [] and no () so is probably ok.

Then you can access any element by doing something like:

temp[4].val[32]

which returns the 33rd value from the 5th temp object, and for its time:

temp[4].val.time[32]

These, however, might show the bug, so you might have to do:

private t = temp[4]

t.val.time[32]

Link to comment
Share on other sites

Progress!

temp[4].val = history.actualTempC actually does work

and so does temp[4].val.time[32]

graphing Temp[4].val works

adding a new value to the sensor with Temp[4].val.addvalue(InsertTime(21.4,systime(),-1) works actually

InsertValue(Temp[4].val,0,21.4) fails with "The Bug", saying channel or function not found, same for DeleteValue

Unfortunately, private t = Temp[4], and InsertValue(t.val,0,21.4) also fails. But since AddValue works I think I have everything I need.

One last thought though, the OOP class functions are a major feature of your program, so hopefully The Bug will get addressed some day!

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.