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