gclib  437
Communications API for Galil controllers and PLCs
arrays.c
Go to the documentation of this file.
1 
9 #include "gclibo.h"
10 #include <stdlib.h> //atoi, atof
11 #include <string.h> //strcpy
12 #include <stdio.h> //fopen
13 #include <math.h> //log()
14 
15 #include <zlib.h> //GSetupDownloadFile(), https://www.zlib.net/
16 
19 {
20  char name[16]; //copy of array name
21  char* data; //pointer to the ASCII array data
22  int len; //length of data
23  int elements; //found data elements for properly dimensioning on download
24  int index; //access index into data for write/read operations
25 
26  struct H_ArrayData* next; //pointer to next ArrayNode in the list
27 
28  //The following fields are only valid on the head node
29  struct H_ArrayData * tail; //if this node is the head node, last ptr will be maintained for faster tail insertion
30  int count; //if this node is the head node, count will be maintained for total number of arrays
31  //note, head node also holds array data
32 };
33 typedef struct H_ArrayData ArrayNode;
34 
37 {
38  node->count = 0;
39  node->data = 0;
40  node->index = 0;
41  node->len = 0;
42  node->name[0] = 0;
43  node->next = 0; //null indicates end of list
44  node->tail = 0;
45  node->elements = 0;
46  //could memset to zero...
47 }
48 
50 GReturn H_AddArray(ArrayNode* head, char* name, char* data)
51 {
52  ArrayNode* node; //the node to fill with data
53  if (head->count == 0) //no need to malloc, just fill the head
54  node = head;
55  else
56  {
57  node = malloc(sizeof(ArrayNode));
58  if (node) //malloc ok
59  H_InitArrayNode(node);
60  else
61  return G_BAD_FULL_MEMORY; //malloc failed
62  }
63 
64  node->data = data; //copy pointer
65  strcpy(node->name, name); //copy name array
66  node->len = strlen(node->data); //output of GArrayUpload is null terminated.
67 
68  head->count++; //count the node we just made
69  head->tail->next = node; //link the new node. If node == head this breaks the last-node-next-null guarantee
70  node->next = 0; //enforce the last-node-next-null guarantee
71  head->tail = node; //update head's tail pointer
72 
73  return G_NO_ERROR;
74 }
75 
78 {
79  if (node == 0) return; //recursive exit condition
80  free(node->data); //free this node's data
81  H_FreeArrays(node->next); //let downstream nodes recursively free their data
82  free(node->next); //free the struct this node points to
83  //no need to free node, the head is declared on the stack
84 }
85 
88 {
89  GReturn rc = G_NO_ERROR; //return code
90  char* array_buf; //buffer to hold array as it's uploaded
91  if (!(array_buf = malloc(MAXARRAY))) //allocate memory for single array upload
92  return G_BAD_FULL_MEMORY;
93 
94  if ((rc = GArrayUpload(g, name, G_BOUNDS, G_BOUNDS, G_CR, array_buf, MAXARRAY)) != G_NO_ERROR) //get this array's data
95  return rc;
96 
97  return H_AddArray(head, name, array_buf); //push the data into the array linked list
98 }
99 
102 {
103  char* array_buf; //buffer to hold array data
104  if (!(array_buf = malloc(MAXARRAY))) //allocate memory for single array upload
105  return G_BAD_FULL_MEMORY;
106  array_buf[0] = 0; //null terminate so len is correct when added
107  return H_AddArray(head, name, array_buf); //push the data into the array linked list
108 }
109 
112 {
113  int len = strlen(element);
114  if ((len + node->index + 1) >= MAXARRAY) //+1 for \r
115  return G_BAD_FULL_MEMORY;
116 
117  strcpy(node->data + node->index, element); //copy the data to the array
118  node->index += len;
119  node->data[node->index++] = '\r'; //delim
120  node->data[node->index] = 0; //null terminate
121  node->len = node->index; //maintain len field
122  node->elements++; //count the element just added
123  return G_NO_ERROR;
124 }
125 
127 
135 {
136  ArrayNode* node = head;
137  GReturn rc = G_NO_ERROR;
138  char command[32]; //buffer for holding command calls
139  while ((node != 0) && (rc == G_NO_ERROR))
140  {
141  //*** Start array table modification
142  // Deallocate the array in case it's the wrong size.
143  sprintf(command, "DA %s[]", node->name);
144  if ((rc = GCmd(g, command)) != G_NO_ERROR)
145  return rc;
146 
147  // Dimension the array with the correct length.
148  sprintf(command, "DM %s[%i]", node->name, node->elements);
149  if ((rc = GCmd(g, command)) != G_NO_ERROR)
150  return rc;
151  //*** End array table modification
152 
153  rc = GArrayDownload(g, node->name, G_BOUNDS, G_BOUNDS, node->data); //download the array
154  if (!fail && rc == G_BAD_RESPONSE_QUESTION_MARK) rc = G_NO_ERROR; //reset return code if we get a ? and fail is 0
155  node = node->next;
156  }
157  return rc;
158 }
159 
162 {
163  if (head->count == 0) //nothing to do
164  return G_NO_ERROR;
165 
166  FILE *file; //file pointer
167  size_t bytes; //length of data to write
168  size_t bytes_written; //bytes actually written to file
169  int colcount = 0; //column counter, used to prevent a trailing colon
170  int data_left = head->count; //counter for number of arrays that still have data left to be written
171  ArrayNode* node = head; //pointer to an array node in the list
172 
173  if (!(file = fopen(file_path, "wb"))) //open file for writing, binary mode
174  return G_BAD_FILE;
175 
176  //write the header
177  do
178  {
179  bytes = strlen(node->name);
180  bytes_written = fwrite(node->name, 1, bytes, file);
181  colcount++;
182 
183  if (colcount != head->count) //write a comma if it's not the last column
184  {
185  bytes_written += fwrite(",", 1, 1, file);
186  bytes++;
187  }
188  else //write a carriage return
189  {
190  bytes_written += fwrite("\r", 1, 1, file);
191  bytes++;
192  }
193 
194  if (bytes_written != bytes) //ensure we wrote what we wanted
195  {
196  fclose(file);
197  return G_BAD_FILE;
198  }
199  node = node->next;
200  } while (node != 0);
201 
202 
203  //now write the data
204  while (data_left) //continue writing rows as long as arrays have data to write
205  {
206  node = head;
207  colcount = 0;
208  do //write one row
209  {
210  bytes_written = 0;
211  bytes = 0;
212  if (node->index != node->len) //data available
213  {
214  while ((node->data[node->index] != '\r') //search for the carriage return delim
215  && (node->index < node->len)) //unless we reach the end
216  {
217  if (node->data[node->index] != ' ') //don't keep spaces
218  {
219  bytes_written += fwrite(node->data + node->index, 1, 1, file);
220  bytes++;
221  }
222  node->index++;
223  }
224 
225  if (node->index == node->len) //reached the end of this data
226  data_left--; //decrement counter to indicate one less array with data to write
227 
228  node->index++; //jump over \r delim
229  }
230 
231  colcount++; //count the cell we just filled
232  if (colcount != head->count) //write a comma if it's not the last column
233  {
234  bytes_written += fwrite(",", 1, 1, file);
235  bytes++;
236  }
237  else //write a carriage return, even on the last line
238  {
239  bytes_written += fwrite("\r", 1, 1, file);
240  bytes++;
241  }
242 
243  //check for write failure
244  if (bytes_written != bytes)
245  {
246  fclose(file);
247  return G_BAD_FILE;
248  }
249 
250  node = node->next;
251  } while (node != 0);
252  } //while (data_left)
253 
254  fclose(file);
255  return G_NO_ERROR;
256 }
257 
259 GReturn H_ArrayDownloadFromMemory(GCon g, const char* array_data, int fail) {
260  GReturn rc = G_NO_ERROR;
261  int pos = 0; //character position in memory
262 
263  int n = 0; //index into name[]
264  char name[32]; //buffer to parse array name
265 
266  int e = 0; //index into element[]
267  char element[32]; //buffer to parse csv element cell data
268 
269  char c = array_data[pos]; //first char of array data
270 
271  //Linked list to hold array data as it's organized for download
272  ArrayNode head; //first element (list head) lives on this stack
273  ArrayNode* node; //current node
274  H_InitArrayNode(&head);
275  head.tail = &head; //circular reference
276 
277  while (c != 0)
278  {
279  if (c == ',' || c == '\r')
280  {
281  if (n)
282  {
283  name[n] = 0; //null terminate
284  n = 0; // next time start filling name from start
285  H_CreateArrayNode(&head, name);
286  }
287 
288  if (c == '\r') { //end of line
289  pos++;
290  break; //done reading headers
291  }
292  }
293  else
294  {
295  name[n++] = c;
296  }
297 
298  pos++;
299  c = array_data[pos];
300  }
301 
302  c = array_data[pos];
303  node = &head;
304 
305  while (c != 0)
306  {
307  if ((c == ',') || (c == '\r'))
308  {
309  if (e) //if anything read into element
310  {
311  element[e] = 0; //null terminate
312  H_ArrayAddElement(node, element);
313  e = 0; //start writing at start of element on next pass
314  }
315  node = node->next; //go to the next array
316  if (node == 0) node = &head; //wrap around to front
317  }
318  else
319  {
320  element[e++] = c;
321  }
322 
323  pos++;
324  c = array_data[pos];
325  }
326 
327  rc = H_DownloadArraysFromList(g, &head, fail);
328  H_FreeArrays(&head); // don't forget to free memory
329 
330  return rc;
331 }
332 
334 GReturn H_DownloadData(GCon g, const char* data, int fail)
335 {
336  GReturn rc = G_NO_ERROR;
337 
338  int pos = 0, n = 0; //position in data, index in cmd[]
339  char c = data[pos];
340  char cmd[32]; //buffer for the command to send
341 
342  while (c != 0)
343  {
344  if (c > 31 && c < 127) //"printable" ASCII content
345  {
346  cmd[n] = c;
347  n++;
348  }
349  else if (c == '\n')
350  {
351  cmd[n] = '\0'; //null terminate
352  n = 0; //back to beginning for next time
353  rc = GCmd(g, cmd);
354  if (rc && (fail || rc != G_BAD_RESPONSE_QUESTION_MARK))
355  return rc; //if fail is 0 and response is a ?, don't error out
356  else
357  rc = G_NO_ERROR; //reset return code
358  }
359 
360  pos++;
361  c = data[pos];
362  }
363  return rc;
364 }
365 
367 char* H_FindSector(char* arr, int arr_size, int index) {
368  int i;
369  for (i = 0; i < arr_size; i++)
370  {
371  if ((int)arr[i] == index)
372  return &arr[i + 1];
373  }
374 
375  return NULL;
376 }
377 
379 {
380  FILE *file;
381  GReturn rc = G_NO_ERROR;
382 
383  if (!(file = fopen(file_path, "rb"))) //open file for reading, binary mode
384  return G_BAD_FILE;
385 
386  // Find the length of the file
387  fseek(file, 0, SEEK_END);
388  int in_len = ftell(file);
389  rewind(file);
390 
391  // Read in all the data to memory
392  char* in_buf = (char*)malloc(sizeof(char) * in_len);
393  fread(in_buf, sizeof(char), in_len, file);
394 
395  fclose(file);
396 
397  // Send array data to the controller
398  rc = H_ArrayDownloadFromMemory(g, in_buf, 1);
399 
400  // Free allocated memory
401  free(in_buf);
402 
403  return rc;
404 }
405 
407 {
408 
409  GReturn rc = G_NO_ERROR; //return code
410  long bytes; //strlen placeholder
411  char array_names[1024]; //buffer to hold copy of names, or response to list arrays LA
412  char name[32]; //buffer to hold a single array name
413  int i, n; //indices
414  char c; //holder for the char currently being read
415  int bracket = 0; //increments when [ seen on a line, [ marks the end of the array name
416 
417  //Linked list to hold array data
418  ArrayNode head; //first element (list head) lives on this stack
419  H_InitArrayNode(&head);
420  head.tail = &head; //circular reference
421 
422  if (names == 0) //check for null pointer in arg
423  bytes = 0;
424  else
425  bytes = strlen(strcpy(array_names, names));
426 
427  if (bytes == 0) //null or "", need to get the arrays from the controller
428  {
429  if ((rc = GCmdT(g, "LA", array_names, sizeof(array_names), 0)) != G_NO_ERROR) //Trimming command, get names from List Arrays (LA)
430  return rc; //no mallocs yet, so we can exit without free
431 
432  bytes = strlen(array_names); //count the response
433  }
434 
435  n = 0; //n is name[] index
436  for (i = 0; i < bytes; i++)
437  {
438  c = array_names[i];
439 
440  if (c == '[')
441  bracket++; //[ marks the end of the array name
442 
443  if ((c != ' ') && (c != '\r') && (c != '\n') && !bracket)
444  {
445  name[n++] = array_names[i]; //keep the char
446  }
447 
448  if ((c == ' ') || (c == '\r') || (i == bytes - 1))
449  {
450  if (n) //if we have anything in name
451  {
452  name[n] = 0; // null terminate name
453  n = 0; // next time start filling name from start
454  bracket = 0; //forget any brackets we've seen
455 
456  if ((rc = H_UploadArrayToList(g, &head, name)) != G_NO_ERROR) //Add data to list
457  {
458  H_FreeArrays(&head); // don't forget to free memory
459  return rc;
460  }
461  }
462  continue;
463  }
464 
465  }
466 
467  //By here, all the array data is in the linked-list starting at head.
468  rc = H_WriteArrayCsv(&head, file_path);
469  H_FreeArrays(&head); // don't forget to free memory
470  return rc;
471 }
472 
473 GReturn GCALL GSetupDownloadFile(GCon g, GCStringIn file_path, GOption options, GCStringOut info, GSize info_len) {
474  GReturn rc = G_NO_ERROR;
475  int fail = 1; //stop on error from controller
476  FILE *file;
477  int i;
478  long in_len; //length of file
479 
480  file = fopen(file_path, "rb");
481  if (file == NULL) return G_BAD_FILE;
482 
483  // Find the length of the file
484  fseek(file, 0, SEEK_END);
485  in_len = ftell(file);
486  rewind(file);
487 
488  // Read in all the compressed data
489  unsigned char* in_buf = (unsigned char*)malloc(sizeof(unsigned char) * in_len);
490  fread(in_buf, sizeof(unsigned char), in_len, file);
491 
492  fclose(file);
493 
494  // Determine the uncompressed data length and allocate an array to store it
495  uLong out_len = (unsigned int)((in_buf[0] << 24) | (in_buf[1] << 16) | (in_buf[2] << 8) | (in_buf[3]));
496  unsigned char* out_buf = (unsigned char*)malloc(sizeof(unsigned char) * out_len);
497 
498  // Uncompress the data and store it in the allocated array
499  rc = uncompress(out_buf, &out_len, in_buf + 4, in_len - 4);
500  free(in_buf); //we're done with the compressed version
501  if (rc)
502  {
503  free(out_buf);
504  return G_BAD_FILE;
505  }
506 
507  // Return the controller info if an info array pointer is provided
508  if (info != NULL && info_len > 0) {
509  char* arr = H_FindSector((char*)out_buf, out_len, 1);
510  if (arr == NULL)
511  {
512  free(out_buf);
513  return G_BAD_FILE;
514  }
515 
516  for (i = 0; i < info_len; i++)
517  {
518  info[i] = arr[i];
519 
520  if (arr[i] == '\0') //end of info sector
521  break;
522  else if (i == (info_len - 1))
523  {
524  info[i] = '\0'; //force null termination
525  }
526  }
527  }
528 
529  // Determine which GCB sectors contain info and return the bit mask
530  if (options == 0)
531  {
532  for (i = 1; i < 7; i++)
533  {
534  if (i == 1 || i == 3)
535  continue; // Skip reserved sectors
536 
537  char* arr = H_FindSector((char*)out_buf, out_len, i);
538  if (arr == NULL)
539  continue; // Continue if sector not found
540 
541  if (arr[0] != '\0') //non-empty sector
542  rc += (1 << (i - 1));
543  }
544  return rc;
545  }
546 
547  // Determine if ignore non-fatal errors bit is set
548  if (options & 0x8000)
549  fail = 0;
550 
551  char* sector;
552  // Options processing
553 
554  int len = out_len - 1; //stop one before the end for null termination
555  for (i = 0; i < len; i++)
556  {
557  sector = (char*)out_buf + i + 1; //start of possible sector
558  if (*sector == 0) //all data is in ascii, so null indicates an empty sector
559  {
560  ++i; //jump over null
561  continue;
562  }
563 
564  switch (out_buf[i])
565  {
566  case 0x02: //parameters sector start
567  if (options & 0x0002) //bit 1
568  rc = H_DownloadData(g, sector, fail);
569 
570  break;
571 
572  case 0x04: //variables sector start
573  if (options & 0x0008) //bit 3
574  rc = H_DownloadData(g, sector, fail);
575 
576  break;
577 
578  case 0x05: //arrays sector start
579  if (options & 0x0010) //bit 4
580  rc = H_ArrayDownloadFromMemory(g, sector, fail);
581 
582  break;
583 
584  case 0x06: //program sector start
585  if (options & 0x0020) //bit 5
586  rc = GProgramDownload(g, sector, NULL);
587 
588  break;
589  }//switch
590 
591  if (rc)
592  break; //exit for
593  }//for
594 
595  // Free allocated memory
596  free(out_buf);
597  return rc;
598 }
int GOption
Option integer for various formatting, etc.
Definition: gclib.h:73
GReturn H_DownloadArraysFromList(GCon g, ArrayNode *head, int fail)
Walks through the array linked list, downloading each.
Definition: arrays.c:134
GReturn GCALL GSetupDownloadFile(GCon g, GCStringIn file_path, GOption options, GCStringOut info, GSize info_len)
Download a saved controller configuration from a file.
Definition: arrays.c:473
unsigned int GSize
Size of buffers, etc.
Definition: gclib.h:72
Structure to create a linked list for array data.
Definition: arrays.c:18
GReturn H_DownloadData(GCon g, const char *data, int fail)
Helper function to send a string of commands to the controller, one at at time.
Definition: arrays.c:334
#define G_BAD_RESPONSE_QUESTION_MARK
Operation received a ?, indicating controller has a TC error.
Definition: gclib_errors.h:66
GReturn H_AddArray(ArrayNode *head, char *name, char *data)
Add an ArrayData node to the linked list.
Definition: arrays.c:50
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:116
void H_InitArrayNode(ArrayNode *node)
Function to initialize the memory of a new node.
Definition: arrays.c:36
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&#39;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)
Uploads a particular array and adds it to the linked list.
Definition: arrays.c:87
void * GCon
Connection handle. Unique for each connection in process. Assigned a non-zero value in GOpen()...
Definition: gclib.h:71
void H_FreeArrays(ArrayNode *node)
Frees all memory downstream of node. After passing list head to this function, all memory is freed an...
Definition: arrays.c:77
GCLIB_DLL_EXPORTED GReturn GCALL GProgramDownload(GCon g, GCStringIn program, GCStringIn preprocessor)
Downloads a program to the controller&#39;s program buffer.
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&#39;s array table.
GReturn H_ArrayDownloadFromMemory(GCon g, const char *array_data, int fail)
Helper function to download a block of arrays to the controller.
Definition: arrays.c:259
int GReturn
Every function returns a value of type GReturn. See gclib_errors.h for possible values.
Definition: gclib.h:70
char * H_FindSector(char *arr, int arr_size, int index)
Function that returns a pointer to the start of the specified sector in the GCB data.
Definition: arrays.c:367
#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:72
const char * GCStringIn
C-string input to the library. Implies null-termination.
Definition: gclib.h:75
GReturn H_ArrayAddElement(ArrayNode *node, GCStringIn element)
Adds an array element to an array node.
Definition: arrays.c:111
#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:378
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:101
GReturn GCALL GArrayUploadFile(GCon g, GCStringIn file_path, GCStringIn names)
Array upload to file.
Definition: arrays.c:406
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:161
char * GCStringOut
C-string output from the library. Implies null-termination.
Definition: gclib.h:74
#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:110
#define G_BAD_FILE
Bad file path, bad file contents, or bad write.
Definition: gclib_errors.h:78
#define G_CR
For GArrayUpload(), use this value in the delim field to delimit with carriage returns.
Definition: gclib.h:39