Skip to main content
Submitted by massey on Fri, 06/27/2008 - 00:31

Hi, I have been up till now using the "Storage Scope" in the Servo design kit to record Torque and Position data on an axis of interest. Firstly I often run out of array room to store the data (i.e it is limited in total number of points it can store). How can this be overcome?

Secondly (and more importantly for my project), I need to record both torque and position for all 6 axes at once (I have DMC 1860 controller, 2 19540 amps, running 6 servo motors at once). I have only managed to collect a single set of data (either torque or position) for 4 axes at once, and again have problems with arrays fulling up too fast and running out of room. The nature of my project means repeating the experiment to collect the other axes is not an option.

Is there a data logging method I can use?

Any help on this would be greatly appreciated,

thank you,

Jonathan

Comments 16

Galil_DJR on 06/27/2008 - 09:02

Yes, there is a method to collect data for all axes. The easiest method is to employ the controller data record and write a simple program to write the data record information to a file.

The new GalilTools Communication Library bundled with GalilTools lite (as well as the other versions) has several API calls to help.

Essentially, what you do is set up the 1860 in DR mode. This sends out the data record at a regular interval (say every 2 ms). Your software then services this data and writes the pertinent data to a file.

The most straight forward solution is in C++:
A preliminary example is here:
http://www.galilmc.com/support/manuals/galiltools/src/cpp/datarecord.ht…

If you need further help, I can give you a hand.

The API reference is here:
http://www.galilmc.com/support/manuals/galiltools/galilx.html

See the Data Record APIs
# Data Record
1. vector sources()
2. void recordsStart()
3. vectorrecord(string method = "QR")
4. double sourceValue(vector record, string source = "TIME")
5. string sourceUnits(string source = "TIME")
6. string sourceDescription(string source = "TIME")

massey on 06/27/2008 - 18:21

excellent thanks for that, sounds very encouraging, will let you know if i have any problems, thanks

MrStraz on 06/30/2008 - 13:48

Hello there,
I tuned in to this channel to inform you of a problem that I believe I have discovered with the software. Up till now, I have been using up the apparent array data capacity of my 2123 controller across cyclic recording of say two variables, thus able to store data for 4000 cycles.
However, any attempt to use the new UPLOAD ARRAY DATA command in GalilTools - which behaves differently to how equiv command did in Smart DMC app - fails. It doesn't save any valid set of array data to a file. The only way to extract that recorded data set from memory of controller I have found is to start up the old Smart DMC app and use its UPLOAD ARRAY DATA command => which brings up the familiar dialog box, then allows one to choose each array individually and then save each to a CSV file.
However, even if the command worked in the new GalilTools as user manual suggests it should (saves all array data to one CSV file), this functionality seems to me would lead to a mess sorting out which numbers are from which array. But in any case, command does not work at all in my latest downloaded 12.0.053157 version of app, so please advise what is going on.
I read of the limited storage cap issue afflicting another user, which has been a gripe of mine, and appreciate knowing now about a get around with the above reply you gave to him. I will try to implement this myself, and although I do not have much knowledge about using API calls and C++ code in this realm, I am going to follow thru on the info you provided and see how I go.

Galil_AndyH on 06/30/2008 - 14:24

The Upload Arrays function should work for all versions of GalilTools. It works exactly as described in the documentation.

Try this:

Download and Execute the following program to the controller.

[code]
DM myArray[10]
i=0
#fill
myArray[i]=i*10
i=i+1
JP#fill,i<10
EN
[/code]

Once you have executed this code, send the command LA to the controller via the terminal. It should respond with

myArray[10]

Then choose Controller/Upload Arrays
Save the file to a known location on your harddrive. The file will be a .csv file with the following information (copied and pasted from my .csv file).

myArray
0
10
20
30
40
50
60
70
80
90

If this process does not work for you, then there is some compatibility problem with GT and your OS/software/etc. Make sure you are using a supported OS (NT is not supported).

E-mail support@galilmc.com with a description of the problem you are seeing. Include your OS and GalilTools version.

Note - the GalilTools version can be found in the Help/About Menu. This should be something like 1.1.0.0 (not 12.0.053157).

The headings of the columns in the csv files are the array names, so there is no problem sorting out the data. You can also use the GalilTools Communication Library to upload specific arrays.

massey on 06/30/2008 - 17:56

Have got galiltools working, i needed to run the new drivers.

Have timeout error when i try to run the 'hello' example, says for me to update firmware, which i d/l'd and am trying to do. In 30mins its still saying 0% complete?

massey on 06/30/2008 - 22:02

Have failed to update any of the firmware onto card. Still comming up with time out error where it takes more than 500ms

Galil_AndyH on 07/01/2008 - 08:09

Give us a call and we should be able to get you going over the phone.
800-377-6329

massey on 07/01/2008 - 16:12

Hi just tried ringing and couldnt get through, maybe the time difference playing its part, im in nz, its 11am here at present

Galil_DJR on 07/02/2008 - 13:10

We are available 8am - 5pm California,USA Time.

MrStraz on 07/03/2008 - 15:45

I notice the other user mr massey was trying to call by phone from Down Unda. Being from that part of the world myself but nevertheless in the US now, I suggest maybe possibly you sir need to know that 800 numbers generally work only within country borders. Call via the std international prefix & country code plus the Galil Cal number after that. And do it today because tomorrow is everyone's Independence Day off!

MrStraz on 07/03/2008 - 15:56

Yesterday I sent in an email to Galil Support following thru with my data on the GalilTools ARRAY UPLOAD cmd problem I encountered, and await response on that. Meanwhile I am trying to implement this Data Record => Write to File solution suggested to user mr massey, which I could certainly do with myself. I investigated what I needed to do, so after installing Visual C++ Express 2008, then installing the Windows Server 3.1 PSDK, I think I am close to getting a handle on things. But when I look up the API reference material you suggested, I see that I need to have a header file Galil.h which supposedly is contained in a directory path C:\Program Files\Galil\GalilTools-x86\lib that doesn't exist on the subject lab machine which has GalilTools app installed. Please assist.

Galil_DJR on 07/03/2008 - 17:11

You probably don't have version 1.1.0.0 installed. 1.0.0.0 does not include the api.

Please install 1.1.0.0 here:
http://www.galilmc.com/products/software/galiltools.html

I have posted the logging utility for both of you here:
http://galilmc.com/ftp/.djftp/

Utility for caching data record information.
Usage: record.exe num_records DR_value csv_file source1 source2 ... sourceN
Example: record.exe 1000 2 data.csv TIME _RPA
Dialog will open to choose connection
Alternate Usage: record.exe sources?
Dialog will open to choose connection and valid sources will be printed.

C:\Documents and Settings\dj\Desktop\Data Record>record 10 8 data.csv TIME _TPA
_TTA _TPB _TTB
1.1.0.0 May 16 2008 18:25:43
192.168.1.13, DMC2182 Rev 1.0p, 1, IHA
TIME is a valid source. Sample counter in samples.
_TPA is a valid source. Axis A encoder position in counts.
_TTA is a valid source. Axis A torque (DAC) in V.
_TPB is a valid source. Axis B encoder position in counts.
_TTB is a valid source. Axis B torque (DAC) in V.
Logging 5 valid sources 10 times to data.csv

Configuring Data Record...
DR started
DR8 set
Records cached:
10
Data logged to data.csv
C:\Documents and Settings\dj\Desktop\Data Record>type data.csv
TIME samples,_TPA counts,_TTA V,_TPB counts,_TTB V
44254,0,0,0,0
44262,0,0,0,0
44270,0,0,0,0
44278,0,0,0,0
44286,0,0,0,0
44294,0,0,0,0
44302,0,0,0,0
44310,0,0,0,0
44318,0,0,0,0
44326,0,0,0,0

C:\Documents and Settings\dj\Desktop\Data Record>

And Here is the Source:
[code]
/*
test record api
*/

#include "Galil.h" //galilx.dll
#include <iostream> //cout
#include <cstring> //strcmp
#include <fstream> //fout - file stream for output
#include <vector>

int main(int argc, char *argv[])
{
if (argc == 2){ //check for sources? switch
if (strcmp(argv[1],"sources?") == 0){//strings equal?
try{
cout << "Valid Controller Sources:" << endl;
Galil g(""); //prompt user for connection
cout << g.connection() << endl; //^R^V
vector<string> s = g.sources(); //get list of valid sources from driver
for(int i = 0; i < s.size(); i++){
cout << s[i] << " " << g.sourceDescription(s[i]) << endl; //print each valid source
}//for
return 0; //finished
}//try
catch(string s){
cout << s;
return 1; //finished with error
}//catch

}//if !strcmp
}// if argc

if (argc < 5) // if not enough args, or not the sources? option .. print usage
{
cout << " Utility for caching data record information." << endl;
cout << " Usage: " << argv[0] << " num_records DR_value csv_file source1 source2 ... sourceN" << endl;
cout << " Example: " << argv[0] << " 1000 2 data.csv TIME _RPA" << endl;
cout << " Dialog will open to choose connection" << endl;
cout << " Alternate Usage: " << argv[0] << " sources?" << endl;
cout << " Dialog will open to choose connection and valid sources will be printed." << endl;
return 0;
}

try{
vector<char> r;
Galil g; //prompt user for connection
cout << Galil::libraryVersion();
cout << endl << g.connection() << endl; //^R^V
int times = atoi(argv[1]); //times from console
string csvfile = argv[3]; //filename from console
string DRval = argv[2]; //Value for DR command
vector<string> s = g.sources(); //get valid sources
vector<string> sources_to_log; //vector for holding valid sources to log

{ // scope for checking valid sources -- keep variable life bounded
bool valid = false; //boolean for valid source
for(int i = 4; i < argc; i++) //walk through tokens on command line
{
valid = false; //set false until we find a valid match
for(int c=0; c < s.size(); c++){ //step though valid sources, search to see if console argument is a valid source
if(argv[i] == s[c]){ // if match found
cout << argv[i] << " is a valid source. " << g.sourceDescription(argv[i]) << " in " << g.sourceUnits(argv[i]) << "." << endl; //print source info
sources_to_log.push_back(argv[i]); //add valid source to log vector
valid = true ; //tag this one as valid
break; // break out of the search for this console token
} // if valid checker
} // for source iterator
if (!valid){//didn't find match, so complain
cout << argv[i] << " is not a valid source, ignoring." << endl;
}
} // for arg iterator
}//end of scope for checking valid sources

//we now have a list of valid sources in sources_to_log

cout << "Logging " << sources_to_log.size() << " valid sources " << times << " times to " << csvfile << endl << endl << "Configuring Data Record..." << endl;
string method = "DR"; // we want to try DR if available
try{
g.recordsStart(); // start DR -- defaults to fastest possible DR (1.1.0.0)
cout << "DR started" << endl;
try{
g.command("DR" + DRval); //see if we can get user's DR request
cout << "DR" << DRval << " set" << endl;
}
catch (string s){//if most inside try fails (DR)
//cerr << s; //send to standard error -- clogs up readout, comment out unless needed
cout << "DR" << DRval << " failed. DR will be " << g.command("MG_DR{Z10.0}") << endl; //notify user
}
}
catch(string s){ //catch for recordsStart()
//cerr << s; //send to standard error -- clogs up readout, comment out unless needed
cout << "Couldn't turn on DR. Falling back to QR Data Record" << endl;
method = "QR"; //set method to QR
}

cout << "Records cached:" << endl;

ofstream fout;
fout.open(csvfile.c_str());
if (!fout){
cout << "Error: File " << csvfile << " could not be opened." << endl;
return 1;
}

//this next for loop prints the CSV column headers
for(int i = 0; i < sources_to_log.size(); i++){ //for each source in vector
fout << sources_to_log[i] << " " << g.sourceUnits(sources_to_log[i]); //print its name and units
if(i == sources_to_log.size() - 1){ //don't print a comma after the last source
continue; //skips comma
}
fout << ","; //print comma for CSV
}
fout << endl; //print \n

r = g.record(method); //get one record -- will disregard - this is to flush buffer
for(int i = 0; i < times; i++){ //now print data the desired number of times
r = g.record(method); //go get a record QR or DR is already decided above
cout << i+1 << "\r";
for(int i = 0; i < sources_to_log.size(); i++){ //for each source in vector
fout << g.sourceValue(r , sources_to_log[i]); //print value out of data record
if(i == sources_to_log.size() - 1){ //dont't print comma after the last source
continue; //skips comma
}
fout << ","; //print comma for CSV
}//for sources to log iteration
fout << endl;
}//for times iteration

fout.close(); //close file
cout << "\nData logged to " << csvfile;
return 0;//logging complete so return

} //try
catch(string s)//catch errors from main try
{
cout << s;
return 1; //error. 0 is normal.
}
} //main

[/code]

massey on 07/06/2008 - 16:32

Excellent thanks for that have got it running. Impressive customer service, thank you.

MrStraz on 07/07/2008 - 14:02

Thank you for this. However, I need to implement a modified version of recording functionality to capture values of computed variables (force sensor analogue input converted) in loops governed by successive cycles of a linear actuator - when its at a certain position each cycle - rather than via sampling period.
I read the following from your GalilTools Comm Lib Ref:
"
Description of the Data Record

The data record is a Galil controller feature that is ideal for data collection and controller monitoring. It is a binary data structure generated by the controller's firmware either in a periodic and asynchronous manner (DR) or via an interrogated, synchronous command (QR). The transmitted data contains a wide array of controller information: encoder positions, reference positions, profiler information, DMC program information, user Variables on the Accelera family . . .
"
I guess this is relevant info, but I don't understand the last phrase. Maybe you could assist or point me to what I need to do from here.

Galil_DJR on 07/08/2008 - 14:52

The Accelera family: 18x6, 40x0, RIO have special variable names which get dispatched in the data record.

For example, if you run the following loop on a 4000:
#setZA
ZAA=TIME
JP#setZA

And then open Watch All and scroll to the bottom under _ZAA, you'll see the time ticks updating as a result.

These can be logged in my example above with:
record 10 8 data.csv TIME _ZAA

This will get you 10 samples of TIME and ZAA, sampled every 8ms.

Galil_DJR on 07/08/2008 - 14:56

If you don't have an Accelera, you can use an unused AUX encoder for integer values:

#setZA
DEA=TIME;'Set Aux encoder register to value in TIME
JP#setZA

Grab the values with
record 10 8 data.csv TIME _TDA