AzeoTech

Administrators
  • Posts

    6,436
  • Joined

Posts posted by AzeoTech

  1. Do you want to be able to arbitrarily load any log file, or do you just want to be able to graph, say, the last week's worth of data?  If you just want to go back, say, under a few million data points then you can leverage the Persist feature of channels to extend History and store the data in an easy to access format separate from your long term data logs.  How far back in time that goes depends on your Channel Timing.  At 1 sec, you can probably go back a good couple weeks.  At 10 seconds, you can go back 6 month probably.

    If you want to be able to arbitrarily load any log file, then you will have to revert to a little script.  Use File.FileOpenDialog() to ask the user what file to open, then use file.open() to open the file.  If it is CSV, you can then use File.ReadDelim() to read the entire file into an array which you can then graph.  Don't forget to close the file when done using file.close().  Something like this:

    private string filename = file.fileOpenDialog()
    if (isempty(filename)) // operator hit cancel
       return   
    endif
    try
       private handle = file.open(filename, 1, 0, 0, 1)
       global fileData = file.readDelim(handle, -1, ",", chr(10), 0)
       file.close(handle)
    catch()
       system.messageBox("Error opening file: " + strLastError)
    endcatch
    

    Now you have a global variable called filedata.  Assuming the csv was created with a logging set, using "DAQFactory time", you can simply plot the desired column vs the first column.  For example, the 1st data column:

    X Expression:  fileData[][0]

    Y Expression: fileData[][1]

     

  2. As mentioned in the previous posts, DAQFactory is not unicode so support for extended character sets is somewhat limited.  The selected font is a big source of problems, but there are still limitations.  Often on has to use images of text instead.  This is something we would like to fix, but there are some significant challenges to ensure backwards compatability.

  3. The problem is that you can't have two channels that reference the same I/O point.  They are considered the same channel and whether the Conversion is applied is unpredictable because one has a Conversion and the other does not.

    If you really want to have a raw and converted channel, then you need to create a regular raw channel for the I/O point, then create a Test D/A channel with Timing = 0, and then put code in the Event for the raw reading that stuffs the converted value into the test channel.  In this case, you can use iif instead of the boolean math.  iif() doesn't work inside a Conversion under the latest version of DAQFactory (something we need to fix...)

    sensor_output_converted.addvalue(iif(button_state), sensor_output_raw[0] + 10, sensor_output_raw[0] + 50)

     

  4. Looks good.  A few pointers:

    1) whenever you think "copy" or "pasted" with code you should be thinking "can I use a function instead?".  In your case, definitely.  So, instead of copying all that code, create a sequence.  Call it, say, "calcColor":

    function calcColor(output, celltarget)
       switch
          case(OUTPUT>=(((V.TIME-shiftstart)/28800)*CELLTARGET))
             return(RGB(0,255,0))
          case(OUTPUT>=(((V.TIME-shiftstart)/32000)*CELLTARGET))
             return(RGB(255,128,0))
          case(OUTPUT<(((V.TIME-shiftstart)/28800)*CELLTARGET))
             return(RGB(255,0,0))
    endcase

    Then, for example, in the event for cell1, you'd do:

    foreColor = calcColor(V.Output1, V.CellTarget1)

    It is slightly slower, processor wise, to do this, but it is SOOOO much cleaner programming wise.  For one thing, if you decide, for example, that you want a slightly different shade of green, you only have to change it in one place.

    2) Calculated V channels, although handy, are slower than simply having the calculation directly.  Likewise, it hides the processor load of the calculation in the V channel.  So, in your case, you reference V.Time, which a normal programmer would think is just a variable, so fast.  But in fact, it is a calculation that is repeated up to 3 times in your case.  As such, you'd be better off calculating V.Time in the code directly and storing in a private variable.  Private variables are much faster than V channels.

    function calcColor(output, celltarget)
       private vtime = floor(systime()%86400)
       switch
          case(OUTPUT>=(((VTIME-shiftstart)/28800)*CELLTARGET))
             return(RGB(0,255,0))
          case(OUTPUT>=(((VTIME-shiftstart)/32000)*CELLTARGET))
             return(RGB(255,128,0))
          case(OUTPUT<(((VTIME-shiftstart)/28800)*CELLTARGET))
             return(RGB(255,0,0))
    endcase

    In fact, I'd probably just subtract shiftstart at the top and apply the multiplier too:

    function calcColor(output, celltarget)
       private precalc = (floor(systime()%86400) - shiftstart) * cellTarget
       switch
          case(OUTPUT>=precalc / 28800
             return(RGB(0,255,0))
          case(OUTPUT>=precalc / 32000
             return(RGB(255,128,0))
          case(OUTPUT< precalc / 28800
             return(RGB(255,0,0))
    endcase

    I'm not sure why you have some things /28800 and some /32000.

    3) there is no reason to floor() the time.  The math works as a floating point since you aren't doing ==.

  5. Looks good.  A few comments:

    1) watch your indentation.  3 spaces in for all blocks (while, if, etc.) and 3 spaces back for the end of the block (endif, endwhile, etc)

    2) if components are named the same you can actually set the same variable on each in one command.  This doesn't work for member functions of the components, but it does for variables.  So if you have three components named MyComponent, you can do:

    component.myComponent.forecolor = rgb(255,0,0)

    and it will set the forecolor on all three components.

    3) an alternate, and often better method is to have the component set its own color through its OnPaint event, but in your case I'd go with what is working!

    4) finally, I'm not sure DAQFactory is going to render across multiple displays in 4k.  I think it will cut off at one display.  You might need to instead create a modeless popup or popups, positioned on the 2nd monitor.

  6. I believe it was added in 19.1.  It is the 5th parameter:

    http.get(sURL, sPath, nPort, sMoreHeaders, bSecure, bGetHeaders)

    sMoreHeaders is a string with extra headers you might want to add to the request

    bSecure is a boolean (0 or 1) to determine if secure

    bGetHeaders is boolean returning the headers from the request instead of the content if true.

  7. Are partcell1 and celltarget1 both Test D to A channels?  And do they have the same D#, I/O type and Channel #?  If so, then they would the same name for identical channels and thus change in tandem.  To fix, just make sure you have different channel #'s on all your test channels.

    A few random things:

    1) your screens look great.  The graphics are very sharp.
    2) why are you using Test channels?  Have you considered just using global variables?  The only advantage of a channel I can think of would be leveraging Persist, but you can do something similar with a variable, either by using a register variable or by writing some script to persist/restore your settings.  The advantage of using a variable would be that you could use an array, "partcell" for example, which would be indexed from 0 to 11 for the twelve options.  Then you wouldn't need execute(), which is slower and not as easy to read
    3) you don't really need all those doubleToStr() calls.  DAQFactory will automatically convert.  So:

    "partcell" + 1 + "[0]"

    will automatically convert 1 to "1" and return "partcell1[0]" because the first item is a string.

    The conversion doesn't work the other way though:

    3+"4"

    will return an error, not 34, nor 7.  However:

    "" + 3 + "4"

    will return "34".

     

  8. The T7 uses the LabjackM device not the UD it appears you are using. These are the names LabJack chose, not us, and should follow their documentation.  Likewise you appear to be using ID #1 but if you are using Ethernet you want to put the IP address in the D# column for T series devices. 
     
    I recommend reviewing the docs on LabJack's website. There are samples for DAQFactory there as well:  https://labjack.com/support/software/examples/ljm/daqfactory
     
     
  9. Yes, unfortunately there are lots of different dialects of SQL.  DAQFactory defaults to MySQL's dialect, but you can tweak it by clicking the SQL button in the logging set next to the data source name.

    Alternatively you can use script and write your own SQL statements.  There are basically three functions you need:

    global dbase = db.open("datasourceName")

    I would do that in a startup sequence.  Although supported there is usually no reason to close the connection to the datasource.

    Once you have a data source handle (in this case "dbase"), you will mostly just use these two functions:

    db.execute(dbase, "<some sql statement>")

    This will execute the SQL statement.  The statement should be one that does not return any data, i.e. most anything except SELECT.  This would be used to post data to the database use the INSERT INTO statement.  To query data, use something like:

    private newData = db.queryToClass(dbase, "select * from mytable")

    This should only really be used for SELECT.  For optimization reasons, this function doesn't work well with complex or agregate SQL statements.  In those cases use db.query() along with the recordset functions.  Everything is in 9.5 of the User's Guide.

  10. You can't create an actual logging set dynamically, though you can edit one with script.  However, you can implement your own logging using the File. functions that give you complete, lower level access to the file system.  Using these functions you can create/open a file and read/write to it however you need to.  These functions are all described in section 9.9 of the User's guide. 

  11. No.  I mean calling the sequence as a function.  The syntax is exactly what I provided.  So:

    startStream()

    will run the code in your startStream sequence as if it was pasted into your calling sequence.  See 5.17 in the user's guide, or check out any intro programming text on functions.  They are a powerful tool that I think will help you in many ways as you progress.

     

     

  12. OK, a couple things to understand:

    1) if you use beginseq() to run a sequence, it starts that sequence in a separate thread.  The other sequence will start, then the calling sequence will immediately move to the next statement.  

    2) There is nothing predictable about how a sequence starts when using beginseq(), nor how Windows will task the calling sequence vs the new sequence (unless the priorities are different, but even then it is more of a suggestion in Windows.  This means that when you do beginseq(x) from sequence y, that x may get a chance to run 1 line, 10 lines or no lines before the sequence y gets to the line after beginseq().  It most cases it is 0.

    3) DAQFactory uses threadpooling, but even then, starting a sequence in a separate thread is an expensive operation, meaning it takes more time than calling a sequence as a function.  Of course, if you need the separate thread, then by all means, but in general I think folks overuse beginseq(), probably because they don't realize they can call the sequence as a function.

    My guess is you just need to replace all you beginseq() in cvpplot with function calls:

    ePut(...)
    delay(1)
    startStream()
    ePut()
    delay(1.2)
    ePut()
    bdc()
    delay(0.1)
    stopStream()

    Note that those last two lines won't execute until bdc() is done and bdc() has a loop, so the stream won't stop until the loop in bcd completes.

    Moving on to your initial problem of counting teeth, I would say that to stop at 175 exactly, even with a slowdown at 131, you probably have a max RPM of about 3000 rpm (one rev (52 teeth) per about 20ms).  It will be easier to implement if you stay well below that.  If you need faster you are going to have to do this closed loop logic in a micro, PLC, or inside the LabJack itself.  This is because, a) DAQFactory runs on Windows and Windows is not preemptive multitasking, meaning it can decide not to give DAQFactory any processor power for 10-20ms without warning, and b) PC->LabJack comms I believe are around 3-4ms for each query.  I don't know how streaming impacts that, but I wouldn't think you could do reliable closed loop control with a loop time of less than 10ms in the best circumstances (i.e. just reading a single input and controlling a single output).  20ms is more reasonable, thus the 3000rpm number.  But as I said, I don't know how streaming is going throw that.  I am guessing it is interrupt driven in the LabJack, so while it is dumping data over the USB, DAQFactory isn't going to be able to read anything else from the LabJack or control anything.  

    You might consider a second LabJack to help, especially if it is not used for streaming, it is Ethernet, and you use Modbus instead of the LabJackM/UD device (i.e. a T4 or T7).  Then you'll bypass the LabJack driver, which is busy handling your first LabJack and streaming, and you'll have a streamlined control loop.

     

  13. Yeah, computers are interesting that way.  Things that you would think are really hard to do, often can be done very easily on a computer, but at the same time, things that you would think are super easy, are actually kind of challenging.  Speech recognition is a prime example.  It is super easy to do as a human, but not so easy for a computer to do.  In your case, the issue is the streaming.  If you slowed everything down it would be a whole lot simpler and you could use your initial logic.  But if you are streaming high speed you have to deal with the issues that come with it, namely that data arrives in blocks, not single values.  This is an issue for any software running on a PC using streaming mode of hardware.

    That said, you might look at using the LabJack's built in Lua scripting to handle the control.  The problem with DAQFactory (and any software running on the PC) is that it takes like 3-4 ms to communicate with the LabJack, thus the need for blocks.  But the Lua script runs on the LabJack itself, so doesn't have this bottleneck.  But then again, you'd have to figure out how to get Lua to work... 

  14. I can't really address what the LabJack does when you try and trigger a single read of an input that you are also streaming.  In general I don't see why you would do that.  It'd be better to simply process the streaming data.  Now, streaming data has issues, namely that DAQFactory only gets the data in blocks.  This is why streaming can go so fast.  The trick is that since the data comes in in blocks that channel[0] is only being updated every blocksize number of points, so always looking at just [0] means you aren't actually looking at all the data.  For this reason it is often best to use aggregate functions to check the entire block.  Something like:

    private lastStart = systime()-1
    private end
    while(1)
       end = systime()
       private datablock = rawCounts[lastStart,end]
       lastStart = end + 0.00001
       if (numrows(datablock) > 0)
          if (max(datablock) > 131)
             ePut(....)
          endif
          if (max(datablock) >= 175)
             break
          endif
       endif
       delay(0.2)
    endwhile

    I do some important stuff here.  One, I subset by time instead of index since I can't predict the block size, nor when this code will execute relative to the block coming in, since that is controlled by the LabJack.  By using time, I can simply request all points since the last loop (or 1 second worth at the start). 

    Second, I capture my end time before using it.  So instead of doing datablock = rawCounts[lastStart,systime()], then doing lastStart = systime(), I capture the time in "end" and use that instead.  This ensures I don't miss data that comes in the time between the datablock line and updating laststart.

    Finally, I use max() to find if any point in the last datablock is > 131 and >= 175.  If you were doing < or <= you would of course want to use min().

    To test this I would recommend avoiding the ePut and just putting a ? statement in:

    if (max(datablock) > 131)
       ? "ePut here"
    endif

    Then you would know the logic works, and then can deal with any hardware issues with trying to do an ePut() while streaming.

  15. The DAQFactory PDF generator only supports a basic set of WMF commands, basically the ones that are used by symbol factory.  Your WMF file uses some complicated ones, specifically:

    POLYPOLYGON16

    which is probably what is being used to draw the parts that aren't showing up in the PDF.  You have a couple choices:

    1) use a much more basic WMF drawing program.  Illustrator and other higher end vector apps tend to just always use the complicated drawing methods.  Simpler, often free, WMF editors tend to only use a small subset of the available commands, and typically the simpler ones.

    2) PolyPolygon is basically a command that takes a bunch of vertices and creates a complicated filled shape.  So, recreate your logo using simpler shapes, namely lines, so that PolyPolygon isn't used.  The non-text part of the logo, for example, should be 8 separate lines, not grouped together as one.  Likewise, the text should be actual text, not shapes.  If you can't get the text to work, eliminate it from the image, and just use DAQFactory's text component to create the label.  As I mentioned, it is possible that your WMF editing app will use polyPolygon just to draw a straight line.  If that happens, you'll need to find a different WMF editor.

     

  16. No, I mean a second physical comm port.  Creating a duplicate comm port in DAQFactory will cause one not to work because Windows only allows one connection to a serial port at once.

    The other thing to watch for is termination. I am assuming you are running RS485 since you are multidrop.  Did you properly terminate both sides of the chain?  Also, the chain has to actually be a chain with only two ends.  You can't do a star arrangement or any branches with RS485.

  17. I'd have to look at the comm monitor window to have any more input.  It is possible that D30 is just really slow to respond. For example, a LabJack T7 Pro set in the highest resolution / bit count takes 157 ms just to do the actual read because it is so high res, so a query would take even longer than that.  If your D30 device is slow, then that could be holding things up.  To find out, set the timing to 0 on both channels, rearrange the windows so the Comm monitor and command / alert window can both be seen at the same time ("tear off" the Comm monitor tab and put it above), then enter:

    read(em_active_power)

    in the command alert window.  You'll see the Tx, and then an Rx.  Look at the difference in time.  Do this a couple times to get an average.  Maybe do the same thing with sht_17_1, just to compare.

    Personally if I was having this issue, I'd get a second comm port and put D30 on it alone so that it didn't cause issues with my other 17 devices.