AzeoTech

Administrators
  • Posts

    6,432
  • Joined

Posts posted by AzeoTech

  1. No, I mean a variable.  See 5.10 in the user's guide.  Its just a place to store a value, kind of like a channel, but simpler.

    As for the sequence, create a new one (5.2 in the user's guide).  Call it, say, "StartUp", and then make sure it is marked Auto-Start (5.22 in the user's guide).

  2. You will need to create a global variable or channel to hold the tare weight.  A variable should work fine.  In a sequence marked Auto-Start put:

    global tareOffset = 0

    Then create a conversion for your weight channel:

    Value - tareOffset

    Then apply that conversion to your weight channel.

    Finally, in your button for setting the Tare, create a Quick Sequence action and put:

    tareOffset = (myWeightChannel + tareOffset)

    where myWeightChannel is the name of the channel you applied the conversion to.

     

  3. No. Only if you try and save the changes to disk.  If you load the prebuilt ctl doc and then modify it but don't save, then it is no issue.  DAQFactory will run with the changes, but of course when you restart you will have to reapply the changes from your config since the ctl never changed.  

    Note of course, that you should also validate your config file to ensure against corruption since it will be read/write.  But this is much easier to do than the ctl doc itself.  Just make sure you don't make any assumptions about the data you are reading in.  And don't use execute() or evaluate() on the data in the file directly.  I've seen folks who will simply read each line of a config file they created and then do execute() on it.  A hacker could just put some bad script in the config file and it would get executed within DAQFactory.

     

  4. DAQFactory scripting, as an interpreted language, is quite slow, however, individual math functions, which are done at a low level, are quite fast.  For example:

    for (i = 0, i < numrows(y), i++)
       y[i] += 5
    endfor

    would be the normal way, in a language like C, to add 5 to every element in the array "y".  However, in DAQFactory this would be quite slow, as you have seen.  Fortunately, in DAQFactory you can do this math with a single line:

    y += 5

    and DAQFactory will apply it to every element in y and do so very rapidly (sub-millisecond, even for thousands of array elements).

    So, the trick is to find ways to avoid the loops.  This sometimes requires some creative functions and/or boolean math.  For example, if I wanted to add 5 to y, but only where y was less than 10, it'd be:

    y += (5 * (y < 10))

    (y<10) is a Boolean expression and in DAQFactory, like C, evaluates to 1 if true, and 0 if false.  y is an array, so y<10 will return an array of 1's and 0's depending on the corresponding element of y.  When multiplied by 5, this results in an array of 5's and 0's which get added to the original y.  Of course adding 0 does nothing, so the result is adding 5 to y wherever y < 10.

    So, to make things faster you need to think about whether you can remove one or both of your loops and replace it will a simpler, single line expression.

    That all said, if you have to have loops, consider two things, both of which apply to all programming languages:

    1) optimize the innermost loop first as it runs the most times

    2) try and remove anything from in the loop that can be precalculated

    In DAQFactory, also know that every line takes time, even an if()/endif, so, for example, a simple optimization I'd do is to replace that internal if():

    if (ck > Vibe_Window_Skut)
       ck=ck-Vibe_Window_Skut-1 ///pri preteceni okna se zacne brat od zacatku
    endif  

    with:

    ck = iif(ck > Vibe_Window_Skut, ck-Vibe_Window_Skut-1, ck)

    iif() is an inline if and returns the 2nd parameter if the first is true, and the 3rd if it is false.

    I would then take that further and drop ck altogether since it is only used in one place, and put the expression there. 

     

     

  5. This works, but it is usually better to create a configuration file of your own that holds this information and then updates the channel table with that data on startup.  Doing SaveDocument() in an automated way can be risky.  If you have a power failure during the save your entire ctl file will get corrupted.  More importantly, in general, for security reasons, it is recommended that your deployed .ctl documents be placed in a read-only folder so they could not be replaced with a modified version by someone who has hacked into the system.  DAQFactory scripting has a lot of power and could do a lot of damage in the wrong hands.

     

  6. We will be addressing streaming with the T series soon.  In the meantime, you probably will need to script the logging into your streaming sequence.  The original sample is this:

    private scanRate = 10000
    device.labjackM.LJM_eStreamStart("ANY", {"AIN0", "AIN1"}, scanRate, scansPerRead, 1)
    private dataIn
    private data
    private st = systime()
    global backlog
    while(1)
       dataIn = device.labjackM.LJM_eStreamRead("ANY")
       data = insertTime(dataIn.data.AIN0, st, 1 / scanRate)
       channelA.AddValue(data)
       data = insertTime(dataIn.data.AIN1, st, 1 / scanRate)
       channelB.addValue(data)
       st += scansPerRead / scanRate
       backlog = dataIn.ljmscanBacklog
    endwhile

    You'd want to make it into something like this:

    private scanRate = 10000
    device.labjackM.LJM_eStreamStart("ANY", {"AIN0", "AIN1"}, scanRate, scansPerRead, 1)
    private dataIn
    private data
    private st = systime()
    global backlog
    global string streamFileName = "c:\myData\streamData.csv"
    private string curFileName = ""
    private handle = 0
    while(1)
       dataIn = device.labjackM.LJM_eStreamRead("ANY")
       data = insertTime(dataIn.data.AIN0, st, 1 / scanRate)
       channelA.AddValue(data)
       data = insertTime(dataIn.data.AIN1, st, 1 / scanRate)
       channelB.addValue(data)
       st += scansPerRead / scanRate
       backlog = dataIn.ljmscanBacklog
       // setup logging:
       // check for change in file name and open file
       if (streamFileName != curFileName)
          if (handle)
             file.close(handle)
          endif
          handle = file.open(streamFileName, 0, 1, 1, 1)
          curFileName = streamFileName
       endif
       data[][0] = seqadd(st, 1/scanRate, numrows(datain.data.ain0))
       data[][1] = datain.data.ain0
       data[][2] = datain.data.ain1
       file.writedelim(handle,data, ",", chr(10))  
    endwhile

    The only trick to this is if you stop this sequence (like you do with stopStream), then the file will still be open.  Use file.closeAll() to close all the open handles.  You could, also, make the file handle and file opening global, and then just have the sequence write to the handle when it is non-zero.

     

     

  7.  Certainly.  But AddChoice is a function, not a variable so you don't use =.  It would be:

    Component.myComboBox.AddChoice(strIn, strIn)

    Note that the function requires two parameters, the first is what is displayed in the combo box.  The second is what is set when the particular item is selected.  If you want them to be the same, you just pass the same thing as I did.

  8. Yes, it looks like an SPCI device on port 5025.  The protocol is SPCI.  You can find the commands on the net as it seems missing from the docs you provided.  I'd start with sending the *IDN? command in the comm monitor and make sure you get a response.  You probably will need to type:

    *IDN?\013

     

  9. Absolutely.  Many people have done very similar applications.  Just collect the two data points from the LabJack at the same data rate and create a graph that plots Force vs Velocity.  Just make sure and set the bottom axis type to Lin instead of Date/Time so that it becomes an XY graph instead of a trend graph.

  10. Yeah, OnLoad really was just designed for the createProperties() and AddListener() functions.  You can't reliably do anything else because you can't predict when it runs.  I am fairly certain it runs after sequences are loaded, but before auto-start sequences are run, but its possible that could change in future releases so I wouldn't want to rely on it. 

    A better, reliable, solution would be to create a message that your components receive that trigger your code, then, once your auto-start sequence is complete, you could trigger that message, and thus the desired code.  7.16 in the user's guide talks about the OnMessage event.  Multiple components can receive the same message from a single SendMessage call, you just have to do an AddListener() function call (mentioned in 7.15) in your OnLoad() for each component.

    The bonus part of that, is that you could, technically, change your Excel sheet, and retrigger the message and the components would update without having to reload the document.

  11. OK, a few things here:

    1) What is the component name?  It is probably best not to rely on the name of the component, but instead create a member variable (property) of the component that you set for each component. (using CreateProperty())

    2) you cannot predict the order in which DAQFactory will start things.  In other words, you have no way to know that the auto-start Sequence is actually going to run before the page is loaded, if the page is also marked the initial page.  So, it is entirely possible that stuff just hasn't been initialized yet because OnLoad is getting called before your sequence runs.  This is why I always only have a single sequence marked auto-start and then start other sequences from there.  In your case, OnLoad is done when the document loads, so you really can't control when it executes, and it almost certainly is executing before the sequence runs.  I'm not exactly sure what you are trying to achieve (use case) so I can't offer an alternative solution.

  12. Yes.  Resolution index 12 is the highest resolution available for the U6.  The higher the resolution, the slower the device becomes.   As seen on their website here:

    https://labjack.com/pages/support?doc=/datasheets/u6-datasheet/appendix-b-noise-and-resolution-tables-u6-datasheet/

    The U6 at resolution index 12 has a sample time of 159 ms PER CHANNEL.  So you have to multiply 159ms * the number of channels and that is the minimum Timing.  Truthfully you need slightly (5ms?) more than that for comms.  If you need faster you either need to use a lower resolution index, or get a second LabJack so DAQFactory can sample both simultaneously.

  13. It might be.  I'd reboot just to be sure and make sure you aren't running any other software that might access the LabJack.  I'd also try using the actual ID of the LabJack as the D#.

    I'd also try a different USB port, preferably a hub without other stuff on it.

    Do the LabJack tools also seem sluggish?

  14. You are right. Your application is not particularly complex, and I see only one minor detail that probably wouldn't cause slow acquisition.  The issue is in your conversions.  Two of them reference channels but without [0] so will end up using the full history in the calculation.

    After fixing that, I'd start by making sure you are running the latest firmware / drivers from LabJack.  Also make sure you don't have any other applications, like LJLogger, etc. running that also access the LabJack.  Then I'd try setting all the channel timing's to 0, and then set just one channel to 2, then try smaller values.

  15. There is no reason to use OpenEx() if you aren't using a password or specifying the driver.  Just use Db.open("Advantech").  Next, make sure you have the same data source name (Advantech) and that that data source can actually get to the database.  Usually the data source configuration has an option to test the connection.  You'll also need to make sure you create it in the proper section.  User DSN's for example are only visible to one user.  Finally, 5.87 is really quite old, probably almost 15 years at this point, and as such may not work with some of the more recent ODBC drivers.  I know that for MySQL you typically have to use the 3.51 ODBC driver.  You might try a different driver.

    That all said, the most common error is to accidently create the ODBC data source in the 64 bit version of the ODBC configuration tool.

  16. I'm assuming DAQFactory was operating normally up until you quit the application?  If so, then the issue is likely with your application.  Something you are doing, probably in one of the sequences, isn't allowing the thread to quit.  Normally DAQFactory will force quit these threads after 5 seconds, but it is possible that a driver you are using has started its own thread that DAQFactory has no control over and that thread is lingering.  What drivers are you calling from DAQFactory?  Have you tried manually stopping all sequences, or perhaps, going to safe mode, before quitting?

  17. Hmm, which version of DAQFactory are you using?  In newer releases you can use date/time specifiers right in the file name and thus make it log every day.  So, you'd do:

    Results_%y_%m_%d.csv

    and then just leave it alone.  DAQFactory should then log it with the date stamp, changing it each night at midnight.

     

  18. Depends on the printer.  Most normal printers with a Windows driver are going to probably wait for a whole page before printing, however, if you can find a serial printer and thus not have to use a windows print driver, then yes, you should be able to send a line and line feed direct to the printer, one line at a time.  I know there are specialized printers for this, such as product printers, but don't have a source, off-hand for a normal paper printer though I am sure they are available.

  19. OK, well, if you just want to give your channels alternate names, use the aliasFor() function.  So, if you have a channel called "MyChannel" and a variable called "MyVariable" that you want to use to reference MyChannel, just call this once:

    MyVariable.AliasFor("MyChannel")

    Now, whenever you access MyVariable, DAQFactory will actually go get the data from MyChannel instead of from the variable.  There is no copying of data so you don't have to worry about your memory.  Note that member functions do not work through this, i.e. MyChannel.ClearHistory() will clear the history of MyChannel, but MyVariable.ClearHistory() won't do anything.

    AliasFor() was originally designed to allow structured data in classes to be mapped to the rather flat channel structure, especially for OPC which has no functions for reading data from script.  But it works for other stuff too!

    But as to your specific question, if you are filling the variable with AddValue, then, yes, there is a history amount.  So, for example:

    global x
    x.HistoryLength = 3
    x.addValue(1)  // x now = 1
    x.addValue(2) // x now = {2,1}
    x.addValue(3) // x now = {3,2,1}
    x.addValue(4) // x now = {4,3,2}

    Note that history only applies when using AddValue().  It does not apply when using InsertAt() or Append() until you do another addValue, so continuing on with my example:

    x.append(5) // x now = {4,3,2,5}, history length has no effect
    x.addValue(6) // x now = {6,4,3}, history length was applied