gclib  367
Communications API for Galil controllers and PLCs
 All Data Structures Files Functions Variables Typedefs Macros Pages
arrays.c
Go to the documentation of this file.
1 
7 #include "gclibo.h"
8 #define _CRT_SECURE_NO_WARNINGS //use traditional C calls like strncpy()
9 #include <stdlib.h> //atoi, atof
10 #include <string.h> //strcpy
11 #include <stdio.h> //fopen
12 #include <math.h> //log()
13 
14 
17 {
18  char name[16]; //copy of array name
19  char* data; //pointer to the ASCII array data
20  int len; //length of data
21  int elements; //found data elements for properly dimensioning on download
22  int index; //access index into data for write/read operations
23 
24  struct H_ArrayData* next; //pointer to next ArrayNode in the list
25 
26  //The following fields are only valid on the head node
27  struct H_ArrayData * tail; //if this node is the head node, last ptr will be maintained for faster tail insertion
28  int count; //if this node is the head node, count will be maintained for total number of arrays
29  //note, head node also holds array data
30 };
31 typedef struct H_ArrayData ArrayNode;
32 
35 {
36  node->count = 0;
37  node->data = 0;
38  node->index = 0;
39  node->len = 0;
40  node->name[0] = 0;
41  node->next = 0; //null indicates end of list
42  node->tail = 0;
43  node->elements = 0;
44  //could memset to zero...
45 }
46 
48 GReturn H_AddArray(ArrayNode* head, char* name, char* data)
49 {
50  ArrayNode* node; //the node to fill with data
51  if (head->count == 0) //no need to malloc, just fill the head
52  node = head;
53  else
54  {
55  node = malloc(sizeof(ArrayNode));
56  if (node) //malloc ok
57  H_InitArrayNode(node);
58  else
59  return G_BAD_FULL_MEMORY; //malloc failed
60  }
61 
62  node->data = data; //copy pointer
63  strcpy(node->name, name); //copy name array
64  node->len = strlen(node->data); //output of GArrayUpload is null terminated.
65 
66  head->count++; //count the node we just made
67  head->tail->next = node; //link the new node. If node == head this breaks the last-node-next-null guarantee
68  node->next = 0; //enforce the last-node-next-null guarantee
69  head->tail = node; //update head's tail pointer
70 
71  return G_NO_ERROR;
72 }
73 
76 {
77  if (node == 0) return; //recursive exit condition
78  free(node->data); //free this node's data
79  H_FreeArrays(node->next); //let downstream nodes recursvely free their data
80  free(node->next); //free the struct this node points to
81  //no need to free node, the head is declared on the stack
82 }
83 
86 {
87  GReturn rc = G_NO_ERROR; //return code
88  char* array_buf; //buffer to hold array as it's uploaded
89  if (!(array_buf = malloc(MAXARRAY))) //allocate memory for single array upload
90  return G_BAD_FULL_MEMORY;
91 
92  if ((rc = GArrayUpload(g, name, G_BOUNDS, G_BOUNDS, G_CR, array_buf, MAXARRAY)) != G_NO_ERROR) //get this array's data
93  return rc;
94 
95  return H_AddArray(head, name, array_buf); //push the data into the array linked list
96 }
97 
100 {
101  char* array_buf; //buffer to hold array data
102  if (!(array_buf = malloc(MAXARRAY))) //allocate memory for single array upload
103  return G_BAD_FULL_MEMORY;
104  array_buf[0] = 0; //null terminate so len is correct when added
105  return H_AddArray(head, name, array_buf); //push the data into the array linked list
106 }
107 
110 {
111  int len = strlen(element);
112  if ((len + node->index + 1) >= MAXARRAY) //+1 for \r
113  return G_BAD_FULL_MEMORY;
114 
115  strcpy(node->data + node->index, element); //copy the data to the array
116  node->index += len;
117  node->data[node->index++] = '\r'; //delim
118  node->data[node->index] = 0; //null terminate
119  node->len = node->index; //maintain len field
120  node->elements++; //count the element just added
121  return G_NO_ERROR;
122 }
123 
125 
133 {
134  ArrayNode* node = head;
135  GReturn rc = G_NO_ERROR;
136  char command[32]; //buffer for holding command calls
137  while ((node != 0) && (rc == G_NO_ERROR))
138  {
139 
140  //*** Start array table modification
141  // Deallocate the array in case it's the wrong size.
142  sprintf(command, "DA %s[]", node->name);
143  if ((rc = GCmd(g, command)) != G_NO_ERROR)
144  return rc;
145 
146  // Dimension the array with the correct length.
147  sprintf(command, "DM %s[%i]", node->name, node->elements);
148  if ((rc = GCmd(g, command)) != G_NO_ERROR)
149  return rc;
150  //*** End array table modification
151 
152  rc = GArrayDownload(g, node->name, G_BOUNDS, G_BOUNDS, node->data); //download the array
153  node = node->next;
154  }
155  return rc;
156 }
157 
160 {
161  if (head->count == 0) //nothing to do
162  return G_NO_ERROR;
163 
164  FILE *file; //file pointer
165  size_t bytes; //length of data to write
166  size_t bytes_written; //bytes actually written to file
167  int colcount = 0; //column counter, used to prevent a trailing colon
168  int data_left = head->count; //counter for number of arrays that still have data left to be written
169  ArrayNode* node = head; //pointer to an array node in the list
170 
171  if (!(file = fopen(file_path, "wb"))) //open file for writing, binary mode
172  return G_BAD_FILE;
173 
174  //write the header
175  do
176  {
177  bytes = strlen(node->name);
178  bytes_written = fwrite(node->name, 1, bytes, file);
179  colcount++;
180 
181  if (colcount != head->count) //write a comma if it's not the last column
182  {
183  bytes_written += fwrite(",", 1, 1, file);
184  bytes++;
185  }
186  else //write a carriage return
187  {
188  bytes_written += fwrite("\r", 1, 1, file);
189  bytes++;
190  }
191 
192  if (bytes_written != bytes) //ensure we wrote what we wanted
193  {
194  fclose(file);
195  return G_BAD_FILE;
196  }
197  node = node->next;
198  } while (node != 0);
199 
200 
201  //now write the data
202  while (data_left) //continue writing rows as long as arrays have data to write
203  {
204  node = head;
205  colcount = 0;
206  do //write one row
207  {
208  bytes_written = 0;
209  bytes = 0;
210  if (node->index != node->len) //data available
211  {
212  while ((node->data[node->index] != '\r') //search for the carriage return delim
213  && (node->index < node->len)) //unless we reach the end
214  {
215  if (node->data[node->index] != ' ') //don't keep spaces
216  {
217  bytes_written += fwrite(node->data + node->index, 1, 1, file);
218  bytes++;
219  }
220  node->index++;
221  }
222 
223  if (node->index == node->len) //reached the end of this data
224  data_left--; //decrement counter to indicate one less array with data to write
225 
226  node->index++; //jump over \r delim
227  }
228 
229  colcount++; //count the cell we just filled
230  if (colcount != head->count) //write a comma if it's not the last column
231  {
232  bytes_written += fwrite(",", 1, 1, file);
233  bytes++;
234  }
235  else //write a carriage return, even on the last line
236  {
237  bytes_written += fwrite("\r", 1, 1, file);
238  bytes++;
239  }
240 
241  //check for write failure
242  if (bytes_written != bytes)
243  {
244  fclose(file);
245  return G_BAD_FILE;
246  }
247 
248  node = node->next;
249  } while (node != 0);
250  } //while (data_left)
251 
252  fclose(file);
253  return G_NO_ERROR;
254 }
255 
256 
258 {
259  FILE *file;
260  GReturn rc = G_NO_ERROR;
261  char name[32]; //buffer for holding name of array
262  int n; //index into name
263  char element[32]; //buffer for holding ascii array element value
264  int e; //index into element
265  char c; //char currently being read
266 
267  //Linked list to hold array data as it's organized for download
268  ArrayNode head; //first element (list head) lives on this stack
269  ArrayNode* node; //current node
270  H_InitArrayNode(&head);
271  head.tail = &head; //circular reference
272 
273  if (!(file = fopen(file_path, "rb"))) //open file for reading, binary mode
274  return G_BAD_FILE;
275 
276  //read out header, making a new ArrayNode for each
277  n = 0;
278  c = 0;
279  while (fread(&c, 1, 1, file))
280  {
281  if ((c == ',') || (c == '\r'))
282  {
283  if (n)
284  {
285  name[n] = 0; //null terminate
286  n = 0; // next time start filling name from start
287  H_CreateArrayNode(&head, name);
288  }
289 
290  if (c == '\r') //end of line
291  break; //done reading headers
292  }
293  else
294  {
295  name[n++] = c;
296  }
297  }
298 
299  //read each line of the file, pushing each cell into its corresponding ArrayNode
300  node = &head;
301  e = 0;
302  while (fread(&c, 1, 1, file))
303  {
304  if ((c == ',') || (c == '\r'))
305  {
306  if (e) //if anything read into element
307  {
308  element[e] = 0; //null terminate
309  H_ArrayAddElement(node, element);
310  e = 0; //start writing at start of element on next pass
311  }
312  node = node->next; //go to the next array
313  if (node == 0) node = &head; //wrap around to front
314  }
315  else
316  {
317  element[e++] = c;
318  }
319  }
320 
321 
322 
323  fclose(file); //done with file
324 
325  //By here, all the array data is in the linked-list starting at head, just download it
326  rc = H_DownloadArraysFromList(g, &head);
327  H_FreeArrays(&head); // don't forget to free memory
328  return rc;
329 }
330 
331 
333 {
334 
335  GReturn rc = G_NO_ERROR; //return code
336  long bytes; //strlen placeholder
337  char array_names[1024]; //buffer to hold copy of names, or response to list arrays LA
338  char name[32]; //buffer to hold a single array name
339  int i, n; //indices
340  char c; //holder for the char currently being read
341  int bracket = 0; //increments when [ seen on a line, [ marks the end of the array name
342 
343  //Linked list to hold array data
344  ArrayNode head; //first element (list head) lives on this stack
345  H_InitArrayNode(&head);
346  head.tail = &head; //circular reference
347 
348  if (names == 0) //check for null pointer in arg
349  bytes = 0;
350  else
351  bytes = strlen(strcpy(array_names, names));
352 
353  if (bytes == 0) //null or "", need to get the arrays from the controller
354  {
355  if ((rc = GCmdT(g, "LA", array_names, sizeof(array_names), 0)) != G_NO_ERROR) //Trimming command, get names from List Arrays (LA)
356  return rc; //no mallocs yet, so we can exit without free
357 
358  bytes = strlen(array_names); //count the response
359  }
360 
361  n = 0; //n is name[] index
362  for (i = 0; i < bytes; i++)
363  {
364  c = array_names[i];
365 
366  if (c == '[')
367  bracket++; //[ marks the end of the array name
368 
369  if ((c != ' ') && (c != '\r') && (c != '\n') && !bracket)
370  {
371  name[n++] = array_names[i]; //keep the char
372  }
373 
374 
375  if ((c == ' ') || (c == '\r') || (i == bytes - 1))
376  {
377  if (n) //if we have anything in name
378  {
379  name[n] = 0; // null terminate name
380  n = 0; // next time start filling name from start
381  bracket = 0; //forget any brackets we've seen
382 
383  if ((rc = H_UploadArrayToList(g, &head, name)) != G_NO_ERROR) //Add data to list
384  {
385  H_FreeArrays(&head); // don't forget to free memory
386  return rc;
387  }
388  }
389  continue;
390  }
391 
392  }
393 
394  //By here, all the array data is in the linked-list starting at head.
395  rc = H_WriteArrayCsv(&head, file_path);
396  H_FreeArrays(&head); // don't forget to free memory
397  return rc;
398 }
Structure to create a linked list for array data.
Definition: arrays.c:16
GReturn H_AddArray(ArrayNode *head, char *name, char *data)
Add an ArrayData node to the linked list.
Definition: arrays.c:48
GCLIB_DLL_EXPORTED GReturn GCALL GCmdT(GCon g, GCStringIn command, GCStringOut trimmed_response, GSize response_len, GCStringOut *front)
Wrapper around GCommand that trims the response.
Definition: gclibo.c:112
void H_InitArrayNode(ArrayNode *node)
Function to initialize the memory of a new node.
Definition: arrays.c:34
GCLIB_DLL_EXPORTED GReturn GCALL GArrayUpload(GCon g, const GCStringIn array_name, GOption first, GOption last, GOption delim, GBufOut buffer, GSize buffer_len)
Uploads array data from the controller's array table.
#define MAXARRAY
Maximum size for an array table upload.
Definition: gclibo.h:35
GReturn H_UploadArrayToList(GCon g, ArrayNode *head, char *name)
Uplaods a particular array and adds it to the linked list.
Definition: arrays.c:85
void * GCon
Connection handle. Unique for each connection in process. Assigned a non-zero value in GOpen()...
Definition: gclib.h:69
void H_FreeArrays(ArrayNode *node)
Frees all memory downsteam of node. After passing list head to this function, all memory is freed and...
Definition: arrays.c:75
GCLIB_DLL_EXPORTED GReturn GCALL GArrayDownload(GCon g, const GCStringIn array_name, GOption first, GOption last, GCStringIn buffer)
Downloads array data to a pre-dimensioned array in the controller's array table.
int GReturn
Every function returns a value of type GReturn. See gclib_errors.h for possible values.
Definition: gclib.h:68
#define G_BAD_FULL_MEMORY
Not enough memory for an operation, e.g. all connections allowed for a process already taken...
Definition: gclib_errors.h:60
const char * GCStringIn
C-string input to the library. Implies null-termination.
Definition: gclib.h:73
GReturn H_ArrayAddElement(ArrayNode *node, GCStringIn element)
Adds an array element to an array node.
Definition: arrays.c:109
#define G_BOUNDS
For functions that take range options, e.g. GArrayUpload(), use this value for full range...
Definition: gclib.h:38
#define G_NO_ERROR
Return value if function succeeded.
Definition: gclib_errors.h:9
GReturn GCALL GArrayDownloadFile(GCon g, GCStringIn file_path)
Array download from file.
Definition: arrays.c:257
GReturn H_CreateArrayNode(ArrayNode *head, char *name)
Creates a buffer on the heap to write data, and adds it to the linked list.
Definition: arrays.c:99
GReturn GCALL GArrayUploadFile(GCon g, GCStringIn file_path, GCStringIn names)
Array upload to file.
Definition: arrays.c:332
GReturn H_WriteArrayCsv(ArrayNode *head, GCStringIn file_path)
After filling the array list, this function is called to write out the CSV.
Definition: arrays.c:159
GReturn H_DownloadArraysFromList(GCon g, ArrayNode *head)
Walks through the array linked list, downloading each.
Definition: arrays.c:132
#define GCALL
Specify calling convention for Windows.
Definition: gclib.h:24
GCLIB_DLL_EXPORTED GReturn GCALL GCmd(GCon g, GCStringIn command)
Wrapper around GCommand for use when the return value is not desired.
Definition: gclibo.c:106
#define G_BAD_FILE
Bad file path, bad file contents, or bad write.
Definition: gclib_errors.h:66
#define G_CR
For GArrayUpload(), use this value in the delim field to delimit with carriage returns.
Definition: gclib.h:39