AzeoTech

Administrators
  • Content Count

    5,832
  • Joined

  • Last visited

Everything posted by AzeoTech

  1. AzeoTech

    Remote channel Persistence

    So, first a short explanation: DAQFactory is used in a very wide variety of applications and as such, a few things that might be feasible to be automatic in certain applications is not automatic in DAQFactory because in other applications it can cause complications. For example, if DAQFactory is running on a remote site with limited bandwidth, accessing the full history from the remote would cause problems. As such, this is not automatic. Instead, you have to use a function to request the data on startup. The function goes something like this: myConnection.GetHistory(channelName, startTime, endTime) The data transfer is async, so the function will return immediately, and once the remote gets the command it will send the data back if available, and put it in the channel locally. This is a somewhat undocumented feature and not often used so I don't remember if it goes to persist. I suggest just giving it a try and see how it works for you.
  2. AzeoTech

    Remote channel Persistence

    What do you mean by a remote license? You mean a remote copy of DAQFactory connecting to another copy of DAQFactory?
  3. AzeoTech

    yes/No button

    Sure, use a Quick Sequence action and the system.messagebox() funciton. Something like this: private string in = system.messageBox("Are you sure?","YESNO") if (in == "Yes") beginseq(general_stop_seq) endif
  4. AzeoTech

    Ethernet connectivity

    This is an outgoing connection to your device, so no firewall issues. Allowing DAQFactory for the firewall would be for incoming, like using DAQFactory as a web server, or for Connections to 2nd instances of DAQFactory. I would do this: 1) reboot the computer 2) start DAQFactory 3) go to Quick - Device Configuration -> New Serial Ethernet device. 4) add a new Ethernet Client, use the IP / Port you think will work. 5) select Monitor to popup the monitor window. Try just typing something and hitting send. If it gives you Port Locked then DAQFactory is unable to open a socket to the device and the issue is outside DAQFactory, or you have the wrong IP/Port.
  5. AzeoTech

    Ethernet connectivity

    Go to the monitor and try just typing one character and then Send. If it comes back "port is busy" then it almost certainly means that DAQFactory was unable to open a socket connection to that IP/Port combination. This sometimes happens when one gets the IP or more likely, the port incorrect. It also happens when the device has a limit on the number of active connections, which most devices do, and you've reached that because you have some other software connecting to the PLC.
  6. Yes, DAQFactory as an interpreted language is not particularly fast. Script is compiled, but it is compiled into pseudo code and it still has to parse symbol names in real time which, relative to true compiled languages is quite slow. A single line of script might take 100 microseconds or more to execute, depending on the processor. But, because it is interpreted means that it can do a lot of things that truly compiled languages won't handle, such as editing parts of the script while the rest of the application is still running. The big disadvantage of course is that it is slow, especially when doing big loops. So, if you needed to add 1 to an array of 1000 values you would normally do something like this in most languages: for (private i = 0, i < numrows(x), i++) x = x + 1 endfor The above of course works in DAQFactory, but on my workstation for example, which is pretty speedy, it still takes 0.115 seconds to do an array of 1000 points. Fortunately DAQFactory has lots of functionality for working with arrays where the work is done at a low level and is super fast, thus avoiding the necessity to create a loop in script. In this case, instead of the code above, I could just do: x = x + 1 and it would do it to the entire array in one single statement. For that, it takes DAQFactory only 75 microseconds on my machine, less actually because I had to time a loop doing the above 100,000 times to get that number, and that means the number includes the time for the for() loop as well. Anyhow, the bottom line is to always try and do operations that can apply to the whole array (and most operators / functions support that) rather than loop through. There are a lot of tricks you can do with boolean math for this sort of thing. For example, to determine if one or more values of x are > 3, you would just do: max(x > 3) In this case, x > 3 returns an array of 1's and 0's, one for each element of x corresponding to whether it is > 3. Then doing max() just tells us if any of those elements are 1 and returns 1 if so, and 0 if not. A couple other useful array functions are search() and filter(). I suggest reviewing 4.5-4.12 of the user's guide when you have a chance.
  7. Well, you could get around it by using a local variable in the component and call evaluate() from the OnLoad(). Then evaluate() is only used once. So, something like: local index = evaluate(componentName) Then you can use index in the expression. Index is just a number so will execute quite fast, especially compared to evaluate(). Note, the OnLoad() is only calculated when the component is created (i.e. when first displayed). You can trigger this event after the fact using component.myComponent.redoLoadEvent() For speed though I would also make sure and do what I said about calculating a combined result for the entire array: MB_Status = MB_Faults + (MB_FaultsAck)*2 + MB_Fires*4 + MB_FiresAck*8+ MB_Inhibit*16 and then the expression in component just becomes: MB_Status[index]
  8. That won't work because ComponentName returns a string. So if ComponentName is "myComponent" doing: MB_Faults[componentName] would be the same as: MB_Faults["myComponent"] not MB_Faults[myComponent] You would have to do: MB_Faults[evaluate(myComponent)] and given the # of elements, you should avoid evaluate() because it is pretty slow.
  9. AzeoTech

    Transfer PRO license from USB key to PC

    You can't. Once the license is on the key, it is there permanently. But of course you can move the key around. You might look into a USB- Ethernet device, such as the SEH UTN Manager which allows you to access the USB key over Ethernet.
  10. There should not be problems with that upgrade, but truthfully I'm not sure your approach is the best anyway. For one thing, you can't index on a string, so: MB_Faults[ComponentName] won't work because componentName isn't a number. Also, I would use array math to combine your entire array of bits into the bytes as you are doing on an individual basis. So where you are doing: MB_Faults[x] + (MB_FaultsAck[x])*2 + MB_Fires[x]*4 + MB_FiresAck[x]*8+ MB_Inhibit[x]*16 you can actually just do: MB_Status = MB_Faults + (MB_FaultsAck)*2 + MB_Fires*4 + MB_FiresAck*8+ MB_Inhibit*16 assuming all 5 of those variables are arrays of the same size and they line up. Do this in some sequence to keep it up to date, then you can just use: MB_Status[x] A better way to do all this is not to use the component name, but rather to use a component property which you create in the OnLoad event using CreateProperty() or CreateStringProperty(). These functions create a property of the component name that you can edit from script or the docking properties window (View-Properties) and that get saved with the document. You could have one that holds the index into the MB_Status array. If I then had a bunch of components to create that are the same except for, say, an index property, I would create the first one with the index property, call it, say MyComponent0, then duplicate it the required number of times. This will auto-name the components MyComponent1, MyComponent2, etc. Then I would create a simple sequence to set the "index" property: for (private i = 0, i < 100, i++) // 100 components, 0-99 execute("Component.MyComponent" + i + ".index = i") endfor You can use a similar script to change other properties of all the controls.
  11. There are ways around stabilized temperature. Just use another step, i.e. step 1 sets the setpoint and moves to step 2. Step 2 waits for the temp to stabilize in range, then sets starttime and moves to step 3. Or just combine step 1 and step 2. My example was just a simplified version. I strongly recommend against putting those while loops inside the case, but it is up to you. I've seen it done with loops inside of states inside of loops many times in the past with customers needing help and it always causes issues. They'll get it to work, then as soon as they go and tweak something it stops working and they just end up adding more embedded code. Same goes with using WaitUntil(). There is no way to break out of it, or to check on the system while waiting other than stopping the sequence. So, for example, you are doing waituntil() the soak time is complete, but you have no way of monitoring the temperature at the same time and correcting. You have to wait until the waituntil() is complete to do anything. If you don't like using starttime, that is fine, but I still recommend using a single while() and monitoring the system time against your calculated end time with each loop iteration.
  12. I'm not quite sure what you mean. Do you mean retrieve the name of the component from script inside the component (like a Quick Sequence or Event)? If so, there is a local variable called "ComponentName" that you can access from inside the component. Or do you mean access a component by building its name from strings and variables? In that case, use execute() or evaluate(): execute("component.myComponent" + x + ".strCaption = 'comp #" + x + " caption'")
  13. The syntax on the while() is wrong. The entire condition has to be in parenthesis: while ((C1_PV > CAL1 - 3) && (C1_PV < CAL1 + 3)) Also, I don't put while()'s in my individual steps. Instead I would put it as a step, and let the outer while handle it. Something like (note renumbering of steps): private starttime while (1) switch case(ProfileStep == 0) C1_SP = CAL1 // set the output channel setpoint for the first profile step setpoint starttime = systime() profileStep = 1 case(ProfileStep == 1) if (systime() >= starttime + (SOAK1 * 10)) // move on: profileStep = 2 else if ((C1_PV <= CAL1 - 3) || (C1_PV >= CAL1 + 3)) // When the temperature is within +3 -3 degrees of first point, start timing soak time. if (systime() > starttime + 3) // don't evaluate for first 3 seconds profileStep = 0 // go back to step 0 to restart the timer endif endif endif case(ProfileStep == 2) //...etc... endcase delay(0.1) endwhile Feel free to use partial steps, say 0, 0.5 for half steps, like what I did in 0 and 1 above. Then you can use floor(profileStep) to get the actual step #.
  14. I didn't take a super detailed look, but in general it is usually best when doing step wise (or really state based) procedures, such as recipes, to use a simple step counter, and I don't see that. The general form would be something like: global recipeState = 0 while (1) switch case(recipeState == 0) // do whatever for the first state. if (readyToMoveOn) recipeState = 1 endif case(recipeState == 1) // do whatever for the first state. if (readyToMoveOn) recipeState = 2 endif // etc for each state endcase delay(0.1) endwhile Likewise, instead of using waitUntil() I will typically use a "nexttime" variable which holds the time stamp of when I want to do something next. This allows the code to loop and me to keep an eye on things while waiting a particular amount of time. For example: private nexttime = systime() // do it right away while(1) if (systime() >= nextTime) // do something // and set the next time to do it: nexttime += 600 // in 10 minutes endif // we should also check that we didn't over temp: if (temperature[0] > 100) // overtemp! so break out break endif delay(0.1) endwhile Use that sort of design to handle the timing, combined with the state tracking and case statement and I think you'll find your script will be much neater. If the recipes really are just doing ramps or soaks, then eventually you might want to move to writing subroutines to handle these cases with parameters, and then use arrays, or an array of objects to hold the parameters for each step. But I'd start by hard coding it like you have done.
  15. You'd have to mark the email as html, then use html tags. Something like: semail.strBody = "<b>New values:</b>: " + CRLF
  16. AzeoTech

    antivirus

    I'm afraid I have no idea which anti-virus have problems with DAQFactory, and there is no way we could track that. If an anti-virus has an issue, then that is a problem with the anti-virus software not DAQFactory and you should report to them that their software is giving a false positive.
  17. AzeoTech

    password

    I'm not completely sure. Can you email us the .ctl document along with what you think the password is, then I'll be able to check. Email it to support at azeotech.
  18. Note you can add a new line by doing something like: private string CRLF = chr(13) + chr(10) semail.strBody = "New values: " + CRLF semail.strBody += "Power Fuel: " + HV_Power_Bulk_Fuel[0] + CRLF semail.strBody += "Vent Fan: " + VentFan_Fan2_Vib_DE[0] + CRLF semail.strBody += "Average: " + Mean(PS_2_Flow_Outlet[0,1600]) + CRLF
  19. That is going to depend on the database you are using, not DAQFactory. Test your select statement in your databases user interface / command line tool first to make sure it works, then try it in DAQFactory. Also, I recommend using db.queryToClass() instead of db.Query().
  20. The problem is that you aren't creating the body correctly. This line: semail.strBody = "text " (HV_Power_Bulk_Fuel [0]) (Ventfan_Fan2_Vib_DE[0]) Is invalid. You need to build up the string in the format you want. For example: semail.strBody = "New values: " + HV_Power_Bulk_Fuel[0] + ", and: " + VentFan_Fan2_Vib_DE[0] + ", average: " + Mean(PS_2_Flow_Outlet[0,1600])
  21. Express does not support Email. You would need Starter at a minimum.
  22. AzeoTech

    Remote DF Modbus/TCP Server won't open TCP connection

    I'm not sure. The easiest solution is to simply get a serial to Ethernet converter and have DAQFactory act as the Modbus slave on a serial port, then use the converter to put that comms onto Ethernet on 502. Then you are no longer using Windows networking and all the issues (i.e. firewall, etc) that come with it. You are using Windows serial ports which are quite straight-forward. DAQFactory and Windows won't know that you are then going Ethernet. Since DAQFactory supports any protocol on any transport layer, you can still use the ModbusTCP protocol without having to use a protocol translator, so you are talking a $200 or less device.
  23. AzeoTech

    RAM usage

    Yes, sending the doc would help. Email to support.
  24. AzeoTech

    Graphing Live Fieldlogger Data

    It can be done, though for live readings from anywhere you will need to use a web service such as DAQConnect. Having remote people connect into your system directly creates all sorts of security challenges. If all you want to do is collect and graph the data, you may be able to use DAQConnect exclusively, along with a DAQBridge. The DAQBridge would be setup to poll the field loggers and push the data to DAQConnect where you can then keep historical and offer trends to multiple users. No PC would be required. This assumes your field loggers talk Modbus. If they don't, you'd have to use DAQFactory on a PC instead of a DAQBridge.
  25. There is not, but you can control the visibility with the "visible" variable which exists on every component. You could also control ordering by putting the components on a different page and overlaying the pages. Then when you set the current page with: page.strCurrentPage = "myPage,my2ndPage" you can control the ordering as it paints one page, then the next, so this: page.strCurrentPage = "my2ndPage,myPage" would paint things differently then the first one.