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