| gclib 528 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
20 struct H_ArrayData
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
38 void H_InitArrayNode(ArrayNode* node)
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
79 void H_FreeArrays(ArrayNode* node)
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
89 GReturn H_UploadArrayToList(GCon g, ArrayNode* head, char* name)
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
103 GReturn H_CreateArrayNode(ArrayNode* head, char* name)
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
113 GReturn H_ArrayAddElement(ArrayNode* node, GCStringIn element)
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
136 GReturn H_DownloadArraysFromList(GCon g, ArrayNode* head, int fail)
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
163 GReturn H_WriteArrayCsv(ArrayNode* head, GCStringIn file_path)
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
380 GReturn GCALL GArrayDownloadFile(GCon g, GCStringIn file_path)
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
408 GReturn GCALL GArrayUploadFile(GCon g, GCStringIn file_path, GCStringIn names)
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
H_DownloadArraysFromList
GReturn H_DownloadArraysFromList(GCon g, ArrayNode *head, int fail)
Walks through the array linked list, downloading each.
Definition: arrays.c:136
GArrayDownloadFile
GReturn GCALL GArrayDownloadFile(GCon g, GCStringIn file_path)
Array download from file.
Definition: arrays.c:380
H_ArrayData
Structure to create a linked list for array data.
Definition: arrays.c:20
H_DownloadData
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
GCmdT
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:229
GCon
void * GCon
Connection handle. Unique for each connection in process. Assigned a non-zero value in GOpen().
Definition: gclib.h:92
MAXARRAY
#define MAXARRAY
Maximum size for an array table upload.
Definition: gclibo.h:39
H_AddArray
GReturn H_AddArray(ArrayNode *head, char *name, char *data)
Add an ArrayData node to the linked list.
Definition: arrays.c:52
H_InitArrayNode
void H_InitArrayNode(ArrayNode *node)
Function to initialize the memory of a new node.
Definition: arrays.c:38
G_BAD_RESPONSE_QUESTION_MARK
#define G_BAD_RESPONSE_QUESTION_MARK
Operation received a ?, indicating controller has a TC error.
Definition: gclib_errors.h:70
G_CR
#define G_CR
For GArrayUpload(), use this value in the delim field to delimit with carriage returns.
Definition: gclib.h:53
G_BAD_FILE
#define G_BAD_FILE
Bad file path, bad file contents, or bad write.
Definition: gclib_errors.h:82
GArrayUpload
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.
G_BAD_FULL_MEMORY
#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:76
H_UploadArrayToList
GReturn H_UploadArrayToList(GCon g, ArrayNode *head, char *name)
Uploads a particular array and adds it to the linked list.
Definition: arrays.c:89
GCStringIn
const char * GCStringIn
C-string input to the library. Implies null-termination.
Definition: gclib.h:96
H_FreeArrays
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
GProgramDownload
GCLIB_DLL_EXPORTED GReturn GCALL GProgramDownload(GCon g, GCStringIn program, GCStringIn preprocessor)
Downloads a program to the controller's program buffer.
G_BOUNDS
#define G_BOUNDS
For functions that take range options, e.g. GArrayUpload(), use this value for full range.
Definition: gclib.h:52
GSetupDownloadFile
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
GCALL
#define GCALL
Specify calling convention for Windows.
Definition: gclib.h:38
H_ArrayDownloadFromMemory
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
G_NO_ERROR
#define G_NO_ERROR
Return value if function succeeded.
Definition: gclib_errors.h:13
H_FindSector
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
GSize
unsigned int GSize
Size of buffers, etc.
Definition: gclib.h:93
H_ArrayAddElement
GReturn H_ArrayAddElement(ArrayNode *node, GCStringIn element)
Adds an array element to an array node.
Definition: arrays.c:113
GOption
int GOption
Option integer for various formatting, etc.
Definition: gclib.h:94
H_CreateArrayNode
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
GCStringOut
char * GCStringOut
C-string output from the library. Implies null-termination.
Definition: gclib.h:95
H_WriteArrayCsv
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
GArrayDownload
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.
GCmd
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:223
e
void e(GReturn rc)
A trivial, C++ style return code check used in Galil's examples and demos.
Definition: examples.h:30
GArrayUploadFile
GReturn GCALL GArrayUploadFile(GCon g, GCStringIn file_path, GCStringIn names)
Array upload to file.
Definition: arrays.c:408
gclibo.h
GReturn
int GReturn
Every function returns a value of type GReturn. See gclib_errors.h for possible values.
Definition: gclib.h:91