gclib  423
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  GReturn rc = G_NO_ERROR;
336 
337  int pos = 0, n = 0; //position in data, index in cmd[]
338  char c = data[pos];
339  char cmd[32]; //buffer for the command to send
340 
341  while (c != 0) {
342  if (c > 31 && c < 127) { //"printable" ASCII content
343  cmd[n] = c;
344  n++;
345  }
346  else if (c == '\n') {
347  cmd[n] = '\0'; //null terminate
348  n = 0; //back to beginning for next time
349  rc = GCmd(g, cmd);
350  if (rc && (fail || rc != G_BAD_RESPONSE_QUESTION_MARK)) return rc; //if fail is 0 and response is a ?, don't error out
351  else rc = G_NO_ERROR; //reset return code
352  }
353 
354  pos++;
355  c = data[pos];
356  }
357  return rc;
358 }
359 
361 char* H_FindSector(char* arr, int arr_size, int index) {
362  int i;
363  for (i = 0; i < arr_size; i++) {
364  if ((int)arr[i] == index) return &arr[i + 1];
365  }
366 
367  return NULL;
368 }
369 
371 {
372  FILE *file;
373  GReturn rc = G_NO_ERROR;
374 
375  if (!(file = fopen(file_path, "rb"))) //open file for reading, binary mode
376  return G_BAD_FILE;
377 
378  // Find the length of the file
379  fseek(file, 0, SEEK_END);
380  int in_len = ftell(file);
381  rewind(file);
382 
383  // Read in all the data to memory
384  char* in_buf = (char*)malloc(sizeof(char) * in_len);
385  fread(in_buf, sizeof(char), in_len, file);
386 
387  fclose(file);
388 
389  // Send array data to the controller
390  rc = H_ArrayDownloadFromMemory(g, in_buf, 1);
391 
392  // Free allocated memory
393  free(in_buf);
394 
395  return rc;
396 }
397 
399 {
400 
401  GReturn rc = G_NO_ERROR; //return code
402  long bytes; //strlen placeholder
403  char array_names[1024]; //buffer to hold copy of names, or response to list arrays LA
404  char name[32]; //buffer to hold a single array name
405  int i, n; //indices
406  char c; //holder for the char currently being read
407  int bracket = 0; //increments when [ seen on a line, [ marks the end of the array name
408 
409  //Linked list to hold array data
410  ArrayNode head; //first element (list head) lives on this stack
411  H_InitArrayNode(&head);
412  head.tail = &head; //circular reference
413 
414  if (names == 0) //check for null pointer in arg
415  bytes = 0;
416  else
417  bytes = strlen(strcpy(array_names, names));
418 
419  if (bytes == 0) //null or "", need to get the arrays from the controller
420  {
421  if ((rc = GCmdT(g, "LA", array_names, sizeof(array_names), 0)) != G_NO_ERROR) //Trimming command, get names from List Arrays (LA)
422  return rc; //no mallocs yet, so we can exit without free
423 
424  bytes = strlen(array_names); //count the response
425  }
426 
427  n = 0; //n is name[] index
428  for (i = 0; i < bytes; i++)
429  {
430  c = array_names[i];
431 
432  if (c == '[')
433  bracket++; //[ marks the end of the array name
434 
435  if ((c != ' ') && (c != '\r') && (c != '\n') && !bracket)
436  {
437  name[n++] = array_names[i]; //keep the char
438  }
439 
440  if ((c == ' ') || (c == '\r') || (i == bytes - 1))
441  {
442  if (n) //if we have anything in name
443  {
444  name[n] = 0; // null terminate name
445  n = 0; // next time start filling name from start
446  bracket = 0; //forget any brackets we've seen
447 
448  if ((rc = H_UploadArrayToList(g, &head, name)) != G_NO_ERROR) //Add data to list
449  {
450  H_FreeArrays(&head); // don't forget to free memory
451  return rc;
452  }
453  }
454  continue;
455  }
456 
457  }
458 
459  //By here, all the array data is in the linked-list starting at head.
460  rc = H_WriteArrayCsv(&head, file_path);
461  H_FreeArrays(&head); // don't forget to free memory
462  return rc;
463 }
464 
465 GReturn GCALL GSetupDownloadFile(GCon g, GCStringIn file_path, GOption options, GCStringOut info, GSize info_len) {
466  GReturn rc = G_NO_ERROR;
467  int fail = 1;
468  FILE *file;
469  file = fopen(file_path, "rb");
470  int i;
471 
472  if (file == NULL) return G_BAD_FILE;
473 
474  // Find the length of the file
475  fseek(file, 0, SEEK_END);
476  uLong in_len = ftell(file);
477  rewind(file);
478 
479  // Read in all the compressed data
480  unsigned char* in_buf = (unsigned char*)malloc(sizeof(unsigned char) * in_len);
481  fread(in_buf, sizeof(unsigned char), in_len, file);
482 
483  fclose(file);
484 
485  // Determine the uncompressed data length and allocate an array to store it
486  uLong out_len = (unsigned int)((in_buf[0] << 24) | (in_buf[1] << 16) | (in_buf[2] << 8) | (in_buf[3]));
487  unsigned char* out_buf = (unsigned char*)malloc(sizeof(unsigned char) * out_len);
488 
489  // Uncompress the data and store it in the allocated array
490  rc = uncompress(out_buf, &out_len, in_buf + 4, in_len - 4);
491  free(in_buf); //we're done with the compressed version
492  if (rc) {
493  free(out_buf);
494  return G_BAD_FILE;
495  }
496 
497  // Return the controller info if an info array pointer is provided
498  if (info != NULL && info_len > 0) {
499  char* arr = H_FindSector((char*)out_buf, out_len, 1);
500  if (arr == NULL) {
501  free(out_buf);
502  return G_BAD_FILE;
503  }
504 
505  for (i = 0; i < info_len; i++) {
506  info[i] = arr[i];
507 
508  if (arr[i] == '\0') break;
509  else if (i == (info_len - 1)) {
510  info[info_len] = '\0';
511  }
512  }
513  }
514 
515  // Determine which GCB sectors contain info and return the bit mask
516  if (options == 0) {
517  for (i = 1; i < 7; i++) {
518  if (i == 1 || i == 3) continue; // Skip reserved sectors
519 
520  char* arr = H_FindSector((char*)out_buf, out_len, i);
521  if (arr == NULL) continue; // Continue if sector not found
522 
523  if (i == 5) {
524  if (arr[0] != '\r') rc += 0x0010;
525  }
526  else {
527  if (arr[0] != '\0') rc += (1 << (i - 1));
528  }
529  }
530 
531  return rc;
532  }
533 
534  // Determine if ignore non-fatal errors bit is set
535  if (options & 0x8000) fail = 0;
536 
537 
538  // Options processing
539 
540  for (i = 0; i < out_len; i++) {
541  switch (out_buf[i]) {
542  // Bit 1 (parameters)
543  case 0x02:
544  if (options & 0x0002) {
545  char* arr = H_FindSector((char*)out_buf, out_len, 2);
546  if (arr == NULL) rc = G_BAD_FILE;
547  if (!rc) rc = H_DownloadData(g, arr, fail);
548  if (rc) {
549  free(out_buf);
550  return rc;
551  }
552  }
553  break;
554  // Bit 3 (variables)
555  case 0x04:
556  if (options & 0x0008) {
557  char* arr = H_FindSector((char*)out_buf, out_len, 4);
558  if (arr == NULL) rc = G_BAD_FILE;
559  if (!rc) rc = H_DownloadData(g, arr, fail);
560  if (rc) {
561  free(out_buf);
562  return rc;
563  }
564  }
565  break;
566  // Bit 4 (arrays)
567  case 0x05:
568  if (options & 0x0010) {
569  char* arr = H_FindSector((char*)out_buf, out_len, 5);
570  if (arr == NULL) rc = G_BAD_FILE;
571  if (!rc) rc = H_ArrayDownloadFromMemory(g, arr, fail);
572  if (rc) {
573  free(out_buf);
574  return rc;
575  }
576  }
577  break;
578  // Bit 5 (program)
579  case 0x06:
580  if (options & 0x0020) {
581  char* arr = H_FindSector((char*)out_buf, out_len, 6);
582  if (arr == NULL) rc = G_BAD_FILE;
583  if (!rc) rc = GProgramDownload(g, arr, NULL);
584  if (rc) {
585  free(out_buf);
586  return rc;
587  }
588  }
589  break;
590  }
591  }
592 
593  // Free allocated memory
594  free(out_buf);
595 
596  return rc;
597 }
int GOption
Option integer for various formatting, etc.
Definition: gclib.h:72
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:465
unsigned int GSize
Size of buffers, etc.
Definition: gclib.h:71
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:60
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:70
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:69
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:361
#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:66
const char * GCStringIn
C-string input to the library. Implies null-termination.
Definition: gclib.h:74
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:370
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:398
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:73
#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:72
#define G_CR
For GArrayUpload(), use this value in the delim field to delimit with carriage returns.
Definition: gclib.h:39