AzeoTech

Administrators
  • Posts

    6,436
  • Joined

Posts posted by AzeoTech

  1. It is most likely that you changed two things in debugging and are attributing the solution to the wrong thing because the other thing you did was more minor.  There is, as far as I know, absolutely no connection between the U12 and UE9.  They use different drivers in DAQFactory and they use different drivers from Labjack, and since you were on Ethernet, they wouldn't even share a USB low level driver (which I doubt even if you were on USB for the UE9).  More likely, in the process of doing all this you cycled power on the UE9 which cleared out all the lost connections and opened it up to new connections, or some other little thing.  It goes back to that joke I posted on the forum a few days ago: 4 people get into a car and it won't start.  The electrician says, "It must be the battery", the engineer says "It must be the starter", the chemist says "It must be bad fuel", and the IT guy says "hey guys, why don't we all get out of the car and then get back in and try again!"  It is particularly funny because it is so true.  In computer systems, a reboot (of all systems) solves like 75% of problems. 

  2. You can't put anything but actual numbers or strings inside {} when defining an array.  So: {1,2,3}, or {"a","b","c"} are valid, but {myVar1, myVar2, myVar3} is not.

    I, personally, would consider creating a two dimensional array called simply procedure that has, it looks like, 8 columns and 6 rows?

    global procedure = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}}

     

  3. Looks real sharp!

    You might consider working your way towards using objects in your script.  Pretty much any time you have multiple, identical things in real life that need representing in DAQFactory script, an object/class is the way to go.  You basically create a template (a "class") that defines how the object works, along with member variables that characterize the object, then instantiate the class into objects, one for each real world item you need, usually storing that object in array with the rest of the similar objects.  There are a few very useful things about objects in DAQFactory, over and above the normal usefulness in any programming language:
    1) when you have an object stored in a variable called "myObject" and you do: 

    curObject = myObject

    it does not copy the object, but instead copies the reference.  I really used the wrong word in the previous sentence.  Objects aren't "stored" in variables.  Only the reference to the object is stored and that reference is what gets passed around.  The advantage of this is that you can create a global variable like curObject and reference it to different objects depending on which thing you want to have the UI work with.  This makes drill down super easy and scalable.

    2) there is a function for member variables of objects (and really any variable) called "aliasFor" which makes the variable into an alias for another variable or, more usefully, a channel.  That way you can use member variables in objects, which are very structured, to reference channels, which are quite flat (just a long list).  The general format is:

    myVariable.aliasFor("myChannel")

     

  4. There doesn't appear to be anything wrong with the setup.  So:

    1) reboot.  Maybe repower the UE9 as well.  Make sure you don't have any LabJack software running as the LabJack only supports one connection at a time on these devices.  
    2) create a new document in DAQFactory before loading anything.  Create the UD ethernet connection like you did and add the single channel.  Then see if it communicates.  If it does, then try your original document, but we need to make sure you don't have any interference.
    3) if it doesn't work, make sure you have the latest UD software and firmware

    On a separate note on the U12's.  You really need to switch off of ID 0 as soon as possible. Right now it is working, but it is entirely possible that the next time you start, the system will decide that the first found device is ID 1.  This would almost certainly happen if you ever accidently unplugged either of those U12's.

     

  5. Looking at the numbers in the comm monitor you have ASCII data, so if you uncheck the box that says "Display all chars as ASCII codes" you will better be able to see what the data looks like, and then how to parse it. 

    I also don't recommend creating a user protocol.  Just create the device with a NULL protocol and create a sequence to do the processing.  

    If you can post the ASCII form of the data stream I can better tell you how to process it.

  6. First, you should only use () for function calls, and [] for subsetting.  StrToDouble() is a function.  The fact it works now does not guarantee it will work in future releases.

    So, my understanding is that TypeList is a lookup table?  That you are trying to display a particular element of TypeList based on the value in ConfigurationArray[][10]?  If so, you can't do that on an array like this.  First, remember that configurationArray[][10] is actually a two dimensional array, with 1 row and x columns and you can't a subset only looks at the rows.  But the real problem is that the subsetting of typelist is only going to look at the first element of that array.  You couldn't, for example, do:

    global indices = {2,1,5,4}
    global string typeList = {'a','b','c','d','e','f'}
    ? typeList[indices]

    This will just print 'c' because it only looks at the 2 of indices and ignores the rest.  DAQFactory doesn't understand what you are trying to do, and for good reason.  

    This is, unfortunately, one of the few examples where you can't use built in functions to achieve what you are doing and instead have to resort to a loop.  That said, I've added a feature request to our list for a lookup() function that would work for this.  It is actually a very easy function to implement internally so we can probably squeeze it in real quick.  Email us direct if you'd like a beta.

  7. A few thoughts:

    1) how are you powering the UE9?  Is it connected via USB?  Was it ever connected via USB while DAQFactory was running.

    2) You don't want to use localID 0 for any LabJack.  That means "FirstFound" in DAQFactory.  They are U12's, so won't interfere with the UE9, but still...

    3) can you post the raw settings for the device configuration?  Click on Quick - Device Configuration, select LabJack UD, then instead of clicking ok, click View Raw.  Copy and paste the contents here.

  8. Glad you got it working.  If no data is coming in, then it is a different issue than formatting.  Improper formatting won't really affect the data coming in unless the device rejects it.

    Your story, though, reminds me of a joke I just heard:

    Four people get into a car and the car doesn't start.  The engineer says, "Oh, it must be the starter".  The electrician says, "Oh, it must be the battery".  The chemist says "Oh, it must be something with the fuel".  The IT guy says "Hey guys, let's get out of the car and then get back in and I'll bet it will start working!"

     

  9. You should get data into the channel for Read Holding U16.  You are getting 0x4371 or 17265 decimal.  But this probably isn't what you want.  Are any of the registers actually 16 bit words?

    It is important to understand that byte ordering is largely based on the processor, and sometimes the programming language.  Modbus itself specifies big endian and only defines 16 bit words, meaning the high order byte is first, then the low order byte.  Floating point and 32 bit integer values in Modbus are totally made up by the hardware and do not exist in any Modbus spec.  In pretty much every case I have ever seen in 25 years up till this one, the byte ordering within each word remains big endian, but the ordering of the two registers (the two 16 bit words) will vary between big endian and little endian.  That is why we have Float and Float R Words.  There is also R Bytes, but I have never seen that used because Modbus explicitly species the byte ordering as big endian in the spec even though it doesn't specify anything about 32 bit values. 

    Your case seems to be R bytes and R words, which, as I said, I have never seen.  DAQFactory doesn't have an I/O type for this because it has never come up in 20 years.  How many of these values do you need to read?  That will determine the best course of action. If it is only a few, on a single device, then the script workaround I gave you before will do the job.  If it is a lot of tags, then we can talk about a different solution.

  10. Yes, it looks like they swapped the bytes in each word.  That is a very unusual form.  DAQFactory has three forms it will read directly.  "Float", "Float R Bytes" and "Float R Words".  I would try Float R Bytes.  If that doesn't work, you will likely need to read the value in as two different U16 words in two channels and then reorder things.  Use the From. and To. functions to do this, though really, probably just To.Float().  So, if you had two channels, called, simply First and Second, where First would end up having 0xffff in your case, and Second would have 0x4375, the final float value would be something like:

    to.Float(concat(transpose(from.Word(First),0), transpose(from.Word(Second),0)))

    You might need From.RWord() instead of From.Word(), and / or swap first and second.

     

     

  11. OK, first, I wouldn't put the data back into channels.  Just put them in global variables. 

    Second, you'll want to add a file.read(filehandle) before your readDelim() function so that the header gets read and ignored.  Then strIn won't have "TheTime", etc as the first element.  

    Third, using Var. notation is very deprecated.  You should instead declare variables, so:

    private FileHandle = ....

    private datain = file.readDelim(...)

    If you do a file.read() to remove the header, then you can read the data into a numeric variable instead of a string.

    Now to the meat.  Let's assume that you've done those changes, so now datain is going be something like {{44693.39601,5377808,0}, ... {44693.40212,1450.296,1432.906}}

    The formula to convert from Excel time (decimal days since 1900) to DAQFactory time (seconds since 1970, also called UNIX time) is:

    DFTime = (ExcelTime - 365*70 - 19) * 86400

    We simply subtract the number of days between 1970 and 1900 (365 * 70) including the 19 leap years, then take that and multiply by the number of seconds in a day.

    If the exceltime is in the first column, you can just do:

    datain[][0] = (datain[][0] - 365*70-19) * 86400

    Now, you really want to end up with two global variables, lets say mV and PV, so after converting the time you'll just do:

    global mV = datain[][1]
    global pV = datain[][2]

    Now you just need to assign the time.  You don't want insertTime() because that just creates a constantly incrementing time stamp.  Instead, we need to use column 0:

    mV.Time = datain[][0]
    pV.Time = datain[][0]

    Finally, we need to sort the data so that newest is first, which is exactly what sorttime does:

    mV = sortTime(mV)
    pV = sortTime(pV)

    Now you can graph mV and pV the same way you would any other channel.

    The whole thing is pretty straight forward if you remove my long winded explanations...

     

     

     

     

  12. Just do this:

    1) create a new channel to hold the mean. Let's say you call it "MyMean", though really you should pick a better name.  It should be Device Type "Test" with I/O type "D to A".  The rest doesn't really matter for now

    2) I'm going to assume you are always taking data, but that you want to click a button that takes the mean of the last 5 seconds of data.  To do that, select the Quick Sequence action for the button and put in script like this, where "myReading" is the name of your main channel.

    MyMean = mean(MyReading[systime(), systime() - 5])

    Click this button whenever, and it will add a new mean to the list, moving the old ones back.  By default it will hold 3600 means, but you can change that with the History of the MyMean channel.  

    3) now you can just plot MyMean vs Time on a graph.  Or if you prefer, MyMean vs Point(MyMean) and then set the bottom axis to "Lin" instead of Date/Time.

     

  13. Personally, if you were doing the linearly, I wouldn't do it that way.  I'd still create export sets, but I'd calculate the means on that data at the same time and stick it into a Test channel with some Persist.  The only reason you'd want to go about reading back the data is if you were going to do the tests at random times along with taking other data for other purposes.

  14. Yes, that is what I am proposing.

    There are a couple alternatives:

    1) you can put the inputs from each machine in its own channel group, then use a little script to trigger the reads using channel.readGroup():

    while(1)
       channel.readGroup("myGroup1")
       delay(1)
    endwhile

    You could create a sequence for each group.

    Or, you could create a variable that enables/disables each group:

    while(1)
       if (myGroup1Enabled)
          channel.readGroup("myGroup1")
       endif
       if (myGroup2Enabled)
          channel.readGroup("myGroup2")
       endif
       delay(1)
    endwhile

    It'd be more efficient though to have each group in its own sequence, even if you added the ifs().  This is because you are TCP and DAQFactory can communicate with each simultaneously.  If you were in multidrop serial (i.e. RS485), either locally or via TCP through a serial to ethernet converter, then this wouldn't work because the hardware would limit you.

    Note that you could make the myGroupEnabled variables manually controller, or automatically disable if there is no comms by looking at the timestamp on one of your channels and comparing it to systime().  You could even go so far as change the rate, for example, poll a group at 1 second interval if all is well, but if it times out, switch to 60 second interval until it comes back online.  Really it is up to you, and what is best depends on your use case, which is why DAQFactory doesn't do it automatically.  It just keeps retrying.

  15. Do you have the 5 machines on the same Timing / Offset?  With TCP you can put each machine on a different Offset which will force them onto a different thread.  That should keep one machine that is down from holding up the others. 

    The other option is to add some script to disable machines that are down.  How you do that depends on how you are polling, script vs channel Timing.

  16. A conversion is like a formula that gets applied to a channel.  Unless you have a variable that is a constant, you usually do not specify a channel name in the conversion.  Instead you put "Value".  When you apply the conversion to your channel, the channel's reading will be replaced where Value appears in the formula.  This allows you to apply the same Conversion to multiple channels.

    So, in your case, the conversion should be:

    Value * 100

    and then you should go to your CCP channel and select "1" for a Conversion instead of "None".

     

  17. There are two ways:

    1) you can use a conversion.  So, if the formula is to multiply volts * 100 to get inches of water, the Conversion would be:

    Value * 100

    You would name the conversion then apply it to any channels that it applies to.  This will make all values appear in these units.

    2) you can do the calculation in the graph itself.  This is less efficient, but is useful when you need the value in different units on the page, or you want the user to be able to change the units on the fly and have it apply to historical values as well.  A Conversion only applies to new values.

  18. You would need to look through the Omron Modbus communication manual for the register list.  I took a brief look and there isn't a register explicitly labelled on/off.  The closest is labelled PID On/Off and it is register 11540.  This is presumably a holding register, U16 that you set to either 0 or 1.  I selected the 2 byte mode registers.  If you want the 4 byte, it would then be a holding register U32 at address 3368.  

    That all said, I have not worked with these controllers so can't say for sure if that is the register you want.  

  19. No worries.  I wasn't suggesting you were wrong.  I just wasn't sure what you were doing.  But I will say: with modern computers, the cost of splitting calculations into constituent parts is quite negligible, but the readability gains are significant.  As such, I recommend breaking apart 28800 and 32000 into their parts with recognizable numbers.  I, for one, recognize 3600 as seconds in an hour, so:

    case(OUTPUT >= precalc / ((3600*8) / (100/100)) 
    case(OUTPUT >= precalc / ((3600*8) / (90/100))

    I'd probably use 0.9 instead of 90/100, but you get the idea.  I assure you, in a year you will have totally forgotten why you put 32000 in there and won't recognize it for what it is, but the expressions I put will make it quite obvious.  It also gets you to think through which number to use so you don't make the mistake you did where you put 28800 instead of 32000.

  20. It is a little of a "do you want one tool that does everything not very well, or several tools that do their particular thing quite well?"  DAQFactory can actually do a fair bit of post processing, especially if you don't mind using script.  In fact, in script you can do just about anything, including things that simple data analysis tools might not be able to do (Excel especially, if you want to call it a data analysis tool).  But, we chose awhile ago not to try and recreate the wheel on the analysis side and make DAQFactory both a data acquisition tool AND a data analysis tool.  MatLab and Igor I know tried to do this, adding data acquisition features to their rather powerful data analysis tools and in my opinion it works marginally, mostly because they are experts in analyzing data, not hardware and acquiring it.

    OK, now to the question at hand.  First, you can use the peak markers of a graph and it will give you a center of mass peak find along with an area between the markers with a linear baseline stretching between them.  This is pretty basic but quick.  

    Getting more advanced, the trick really is figuring out where the peaks are, and where the bases of said peaks are. DAQFactory doesn't have a built in peak finding algorithm so you'd have to script your own.  How hard this is really depends on how noisy your signal is.  Most true analysis tools will do a curve fit, then find the peak of the curve.  DAQFactory doesn't have a built in curve fit, so you'd either need to roll your own in script, or use smooth() to iron out the noise.

    Once you have the peak location and base points, it is really easy to calculate height and area.  Area is just sum(), while peak height is just the Y value for the peak.  I'm assuming no baseline, but if you can do a baseline as well, either by simply subtracting out a baseline measurement, or calculating one based on the peak base points (which is what the peak markers do).  

    Now with area, the trick is to incorporate X into it.  If your measurements are spaced evenly in X, then the expression is simply sum(y) * dx where dx is the total difference in x between start and stop of the peak.  If not, then it becomes more like: sum(y*dx) where dx is the difference between consecutive Y points.  If X is time, you can calculate dx from the time part of the channel.

    So, let's say you have a channel called "Chromo" and you figure out that the start time of the peak is at a variable you called "st", while the end time is at "et".  You can subset "Chromo" to just get the data from the peak by doing:

    private peakData = Chromo[st,et]

    Here we are subsetting by time, meaning DAQFactory is getting all the data in Chromo with a timestamp between st and et.  You can also, of course, subset by data point number, which is what we do when we do [0].

    Now that we have the peakData, the area assuming the measurements are NOT equally spaced in time is:

    sum(peakData * (peakData.time[1,100000] - peakData.time[0,100000]))

    Note that 100000 is just some arbitrarily big number so that I get all the time data.  Note how the first one is [1, ...], while the second is [0,....].  That whole part in parenthesis is basically creating an array of deltaT's which we then multiply by each data point in peakData, then finally sum it.

    I can go on and on.  As I said, you can do A LOT of data analysis in DAQFactory using scripting, it just requires some knowhow on data analysis and some scripting.

  21. The mouse pan isn't designed for just going left and right.  For that you should use your own transport controls, be that buttons, a scroll bar or the like.  These would either change the bottom axis scaling directly, or change global variables with the current scaling that the graph references.

  22. The units for Persist (and for that matter History) is points, not seconds.  These are the same thing if your Timing is 1, but if your timing is, say, 10 (seconds), then a History or Perist of 20 means that you have 200 seconds in data.

    Typically the Persist will be the same size as the History or larger.  One would set the Persist the same size so that historical data persists between DAQFactory restarts.  Making the Persist bigger than the history allows you to quickly access data that would otherwise take up too much memory to have in entirely in memory.

    The best way to setup the graph trace is to add [bottomAxis.currentScaleFrom, bottomAxis.currentScaleTo] after your channel, so:

    myChannel[bottomAxis.currentScaleFrom, bottomAxis.currentScaleTo]

    The thing about Persist is that you can only access data in Persist that is past the end of the History if you subset.  This is to prevent you from accidently pulling in the entire Persist data file.  The bottomAxis.currentScaleFrom / To variables are part of the graph and hold the current minimum and maximum values for the bottom axis scaling.  So, by subsetting using these, the graph is only asking the Persist file for data that it needs to do the graph.  Then you can just change the bottom axis scaleFrom / scaleTo to get the desired range.

    Note that you can shift-click and drag a graph to pan it (if it isn't selected).  You can also drag a box, right click in the box and select zoom in or zoom out.  Both of these will "Freeze" the graph so that it doesn't move with new data.  To "Thaw" the graph, you right click in the graph and select Thaw.

    There are other ways to control the graph range, it just depends on what you would like.