| gclib 421 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
18 struct H_ArrayData
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
36 void H_InitArrayNode(ArrayNode* node)
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
77 void H_FreeArrays(ArrayNode* node)
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
87 GReturn H_UploadArrayToList(GCon g, ArrayNode* head, char* name)
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
101 GReturn H_CreateArrayNode(ArrayNode* head, char* name)
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
111 GReturn H_ArrayAddElement(ArrayNode* node, GCStringIn element)
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
134 GReturn H_DownloadArraysFromList(GCon g, ArrayNode* head, int fail)
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
161 GReturn H_WriteArrayCsv(ArrayNode* head, GCStringIn file_path)
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
370 GReturn GCALL GArrayDownloadFile(GCon g, GCStringIn file_path)
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
398 GReturn GCALL GArrayUploadFile(GCon g, GCStringIn file_path, GCStringIn names)
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 }
GOption
int GOption
Option integer for various formatting, etc.
Definition: gclib.h:72
H_DownloadArraysFromList
GReturn H_DownloadArraysFromList(GCon g, ArrayNode *head, int fail)
Walks through the array linked list, downloading each.
Definition: arrays.c:134
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:465
GSize
unsigned int GSize
Size of buffers, etc.
Definition: gclib.h:71
H_ArrayData
Structure to create a linked list for array data.
Definition: arrays.c:18
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:334
G_BAD_RESPONSE_QUESTION_MARK
#define G_BAD_RESPONSE_QUESTION_MARK
Operation received a ?, indicating controller has a TC error.
Definition: gclib_errors.h:60
H_AddArray
GReturn H_AddArray(ArrayNode *head, char *name, char *data)
Add an ArrayData node to the linked list.
Definition: arrays.c:50
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:116
H_InitArrayNode
void H_InitArrayNode(ArrayNode *node)
Function to initialize the memory of a new node.
Definition: arrays.c:36
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&#39;s array table.
MAXARRAY
#define MAXARRAY
Maximum size for an array table upload.
Definition: gclibo.h:35
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:87
GCon
void * GCon
Connection handle. Unique for each connection in process. Assigned a non-zero value in GOpen()...
Definition: gclib.h:70
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:77
GProgramDownload
GCLIB_DLL_EXPORTED GReturn GCALL GProgramDownload(GCon g, GCStringIn program, GCStringIn preprocessor)
Downloads a program to the controller&#39;s program buffer.
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&#39;s array table.
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:259
GReturn
int GReturn
Every function returns a value of type GReturn. See gclib_errors.h for possible values.
Definition: gclib.h:69
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:361
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:66
GCStringIn
const char * GCStringIn
C-string input to the library. Implies null-termination.
Definition: gclib.h:74
H_ArrayAddElement
GReturn H_ArrayAddElement(ArrayNode *node, GCStringIn element)
Adds an array element to an array node.
Definition: arrays.c:111
G_BOUNDS
#define G_BOUNDS
For functions that take range options, e.g. GArrayUpload(), use this value for full range...
Definition: gclib.h:38
G_NO_ERROR
#define G_NO_ERROR
Return value if function succeeded.
Definition: gclib_errors.h:9
GArrayDownloadFile
GReturn GCALL GArrayDownloadFile(GCon g, GCStringIn file_path)
Array download from file.
Definition: arrays.c:370
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:101
GArrayUploadFile
GReturn GCALL GArrayUploadFile(GCon g, GCStringIn file_path, GCStringIn names)
Array upload to file.
Definition: arrays.c:398
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:161
GCStringOut
char * GCStringOut
C-string output from the library. Implies null-termination.
Definition: gclib.h:73
GCALL
#define GCALL
Specify calling convention for Windows.
Definition: gclib.h:24
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:110
G_BAD_FILE
#define G_BAD_FILE
Bad file path, bad file contents, or bad write.
Definition: gclib_errors.h:72
gclibo.h
G_CR
#define G_CR
For GArrayUpload(), use this value in the delim field to delimit with carriage returns.
Definition: gclib.h:39