AzeoTech

Administrators
  • Posts

    6,432
  • Joined

Posts posted by AzeoTech

  1. 1) in general, DAQFactory is not case sensitive.  However, it is case sensitive when you compare strings, so:

    private string a = "abc"
    private string b = "AbC"
    private string c = "abc"

    ? a == b
    ? a == c

    will print 0 because a and b are not the same, and then print 1 because a and c are.

    2) No pan, but in newer releases of DAQFactory you can do Page.scaling = 0.8 (or some other number) to scale the page down to fit.

    3) You can do that, but it is reversed and you need to put 0x in front.  So RGB(255,0,0) is the same as 0x0000ff, and RGB(0,0,255) is the same as 0xff0000.  This is just a side effect of universal standards.  The screen wants red as the low order byte, but since we say "RGB", not "BGR" web designers put the red byte first, as in #ff0000.  

    4) yes, use the execute function:

    for (private i = 1, i < 5, i++)
       execute("component.lg_temp" + i + ".rangeMax = myExp")
    endfor

    Just remember that it is a little slower than typing it out.

  2. OK, first read my post about using edit boxes in HMI's:

    That said, simply do page.updateEdits() to have the edit boxes populate with whatever value is currently in the Set Channel specified.  The reason DAQFactory doesn't automatically do this is that it would be very frustrating to the operator if the edit box changed value while they are trying to enter a new value.

    As to your second question, I'm not quite sure what you are trying to do.  Can you give me some more detail?

     

  3. OK, you'll want to put Filename as a global string variable:

    global string filename = file.opendialog(...)

    also, [] notation in the function call doesn't mean anything.  Just drop it.  If you don't want to specify a parameter, just leave it out.  Most functions have required parameters.

    Create the file when you start the experiment using the filename global variable.

     

  4. You really should just put everything in that sequence, that way everything is synced up.  Use the file. functions to create the log, or perhaps an export set, but I would use the file. functions which give total flexibility.  Record the time stamp when you activate and deactivate the cylinder and then you can subset your load cell reading between those two times.  Then just take the max and log it.  You can probably keep the acquisition of the reading in a Channel with its own timing.

    That all said, be careful about throwing away data.  There is certainly a benefit to creating slim data files, but consider also having a separate raw log for backup.  I always tell this story about the ozone hole: when it was first discovered back in the 80's, it was the find of the century for atmospheric scientists.  A group went down to the south pole, measured ozone for a bit, wrote a paper, and became famous (within the atmospheric scientist community at least).  Well, it turned out that another group had been measuring ozone for years at the pole, but, since data storage was expensive back then, they had programmed their system to throw away any data below 50ppb because it was assumed that that meant the machine was glitching.  Of course, the ozone hole was when ozone was below 50ppb so they totally missed it.  Back then they had a reasonable reason for saving storage space.  Today, when you can buy an 8 terrabyte drive for like $100 there really is no excuse.

    That said, your data might not be critical to warrant logging raw, but whenever I hear about trying to save disk space used in logging slow (i.e. under, say, 1khz) data I have to tell this story.  I mean even at 1khz, a years worth of data on a single channel is only 31 gig.

  5. The max() function will actually return a timestamp of the max point, so:

    private forceMax = max(force)

    forceMax will be the maximum of force, and getTime(theMax) will have the timestamp for that maximum.  You can then use that timestamp to subset displacement.  You'll need a little buffer since the timestamps might not be identical.  It depends on how you are acquiring the data.  For example:

    private dispAtForceMax = (displacement[getTime(forceMax)-0.05, getTime(forceMax)+0.05])[0]

    I picked 0.05.  You should probably use about 1/2 the acquisition time, so if your timing is 0.1, you'd use the 0.05 I used.  The extra [0] at the end is just in case you have two datapoints in that time range.

  6. That function, and a few others, will only execute in the primary thread of DAQFactory which is the one that handles the user interface.  It cannot be triggered from a secondary thread, i.e. a sequence running on its own (vs called as a function from the user interface).  There are several reasons for this, one is related to how Windows does the user interface and the corresponding message pump.  The other is that in general having some random popup appear that isn't triggered by a user interface action is usually bad user interface design, which is why we didn't implement ways around the first reason. 

    You however, could implement it yourself using a flag and the OnPreDraw() or OnPostDraw() system events, which run in the user interface thread, but I would seriously recommend against it.  Having a folder select popup just appear at random isn't going to work very well for the operator.  If you need to have the operator select a folder before running some background rountine, then put the folderSelect command in the button as a Quick Sequence, and then start the sequence afterwards.

  7. Certainly.  Just don't set N_Cycles_executed to 0 in the sequence.  Declare the variable (as I recommend all globals) in a separate sequence marked Auto-Start.  I usually call this sequence Startup.  Then, create three buttons, with Quick Sequence actions:

    Start:
    N_cycles_executed = 0
    beginseq(mysequence)

    Continue:
    beginseq(mysequence)

    Stop:
    endseq(mysequence)

    This, of course, does not deal with the state of the solenoid when stop is clicked, but it does keep it from resetting the counter.  To do a true Pause you would have to use a flag and replace the delay() statements in your script with a while loop that watches the flag as well as the time.

  8. The problem is that there are two different ways of notating Modbus:

    1) using 40,001 notation, where holding registers go from 40001-49999, input registers from 30001-39999, etc.  The problem with this notation is that it isn't real.  It is just documentation.  When you ask for 40001, what is actually sent over the serial / ethernet connection is 0.  The 10,000 place is stripped and then the subtract 1.  This is the original Modbus spec and it was done because at the time they thought electricians couldn't count from 0 like programmers.  Personally I find electricians to be very smart and more than capable of counting from 0, but then they didn't ask me.

    2) using 0 notation, where all registers start at 0 and there is no translation.  In this case, when you ask for holding register 0, what is actually sent is 0.  And when you ask for 40001, what is actually sent is 40001.

    Unfortunately, some manufacturers don't understand this and their documentation is messed up.  It takes some experimentation and testing to figure it out.  

    DAQFactory attempts to automatically handle this for you.  It does this by assuming that when you request a register between 30001 and 49999 that you are using the first, 40,001 based notation.  Until that point it assumes you are using the second, zero based notation.  Where it runs into trouble is what you are seeing, where you want registers in the 30,001-49,999 range but its not in 40,001 notation.  You can see it: 0x0ACF = 2767, which is 32768 with the 3 stripped and subtract 1.

    To keep DAQFactory from using this mode, simply do a single read of a register > 50000.  You can do this in an auto-start sequence.  You only have to do it once.  Add  a try / catch so it doesn't error out:

    try
       device.plc_1.readholdingU16(0,50001,1)
    catch()
    endcatch

     

  9. The actual wiring is wrong?  I'm assuming you are talking 485 which has a +/- and not 232 which has transmit and receive.  If you have transmit and receive backwards you won't get anywhere.  That would be like talking to someone who has their handset backwards and are trying to talk into the speaker.  If the 485 is reversed and it is thinking that on bits are off, and off are on, then, unfortunately, the answer is still no.  For one thing, the bit level serial is handled by the serial hardware and Windows, not DAQFactory.  But in addition to that, serial bits are more than just data.  There is a start bit, a stop bit and sometimes a parity bit as well, and if these are reversed, the whole comms will be messed up.  Unfortunately you really just need to fix the wiring...

     

  10. Any emulator is going to run slower than a real system, and the Pi isn't exactly a powerful computer to start.  That said, if your application isn't very big, it might run just fine.  You can also tune your application to work on it by just being careful. 

    For example, if you have an LED component and a channel called MyChannel and you put "MyChannel" in the LED component expression, the system actually makes a copy of the entire history of MyChannel and then the LED takes just the most recent value.  This isn't usually a big deal on a reasonable PC, unless you had hundreds of LEDs and your history's were large, but on an underpowered system, it is more noticeable.  The correct nomenclature is to put "MyChannel[0]" because then the expression parser knows you only want the most recent data point and so only that point gets copied.

  11. You can certainly use version control.  Version control apps typically work with any type of document.  However, as a binary document, CTL files cannot be diffed, which is certainly one advantage of version control apps, and perhaps why you are hoping to use one.  We have thought about ways we could make DAQFactory documents diff'able and hopefully will implement something soon.  For now, you can get parts of your ctl doc into a diff'able format by doing a few things:

    1) do FIle - Dump script to File and save a copy of all your script and store that along with the ctl doc.  This file is a text file so definitely diff'able.

    2) for channels, go to the channel table and do an Export.  This will create a CSV file which is also diff'able.

     

  12. I haven't tried this, but I will say this: Win10IoT really refers to two completely different products.  There is IoT-Core which is essentially a rebranded Windows CE (Windows Mobile), which is a rather stripped down version of Windows.  Then there is IoT-Enterprise, which is a rebranded Windows-Embedded, and an actual full Windows.  DAQFactory should run on IoT-Enterprise without any issue, provided you haven't stripped it of any necessary features.  IoT-Core, however, DAQFactory won't run on as is.  I don't know anything about the x86 emulation to run full win10 binaries.  It certainly seems feasible, but also is likely very slow.  However, that may not be an issue with your particular application.   DAQFactory is a 32bit app, so will run on x86. 

    You also might consider using the 5.87 version of DAQFactory.  It is built on an older compiler and is a little more streamlined for older / slower systems.

     

  13. First, this function is largely deprecated and may not be available in 20.1.  If you are just trying to smooth a graph, either use the spline line type for the graph, or use smooth() or other averaging. 

    But, that said, this function takes an X / Y trace defined by arrays of X and Y points (X array and Y Array) and then does an interpolation to give you new Y values for the array of values in Interpolate to Array X.  I've provided a simple example.  Note that the function does not work particularly well at the ends.  In fact, I find the function to not work particular well in general, which is why I recommend using other methods for data smoothing.  If you'd like to give me your use case, I can better identify what you should use instead.

    interpolateExample.ctl

  14. You'll want to ask the folks at LabJack about what channel #'s you need to use to stream FIO.  It isn't 1 to 1.  The 0's after the 3 aren't used for that particular command.  The UD uses a generic AddRequest() function that can take up to four numbers.  Only certain commands require those extra numbers, but you need to provide 0's even if not used.

  15. OK, now your problem is line 3 (as indicated).  DAQFactory, like C, JavaScript and really most languages uses one operator for assignment (=) and one for comparison (==).  C and JavaScript and others use these exact operators.  Others, like Pascal, use := for assignment and = for comparison.

    Anyhow, unlike C, you can't do assignment inside an if() statement.

    Three other things:

    1) I would do the assignment for i=0 outside the loop to make the loop run faster, similar to how you did it the first time.  It is negligible in this case since the loop only iterates 4 times, but in larger loops, the if() evaluation would get expensive.

    2) You can do the assignment of profile in the declaration like you had before.  The issue was the []'s in the declaration, not that you did the assignment.