| gclib 354 Communications API for Galil controllers and PLCs |

All Data Structures Files Functions Variables Typedefs Macros Pages
arrays.c
Go to the documentation of this file.
1
7 #include "gclibo.h"
8 #define _CRT_SECURE_NO_WARNINGS //use traditional C calls like strncpy()
9 #include <stdlib.h> //atoi, atof
10 #include <string.h> //strcpy
11 #include <stdio.h> //fopen
12 #include <math.h> //log()
13
14
16 struct H_ArrayData
17 {
18 char name[16]; //copy of array name
19 char* data; //pointer to the ASCII array data
20 int len; //length of data
21 int elements; //found data elements for properly dimensioning on download
22 int index; //access index into data for write/read operations
23
24 struct H_ArrayData* next; //pointer to next ArrayNode in the list
25
26 //The following fields are only valid on the head node
27 struct H_ArrayData * tail; //if this node is the head node, last ptr will be maintained for faster tail insertion
28 int count; //if this node is the head node, count will be maintained for total number of arrays
29 //note, head node also holds array data
30 };
31 typedef struct H_ArrayData ArrayNode;
32
34 void H_InitArrayNode(ArrayNode* node)
35 {
36 node->count = 0;
37 node->data = 0;
38 node->index = 0;
39 node->len = 0;
40 node->name[0] = 0;
41 node->next = 0; //null indicates end of list
42 node->tail = 0;
43 node->elements = 0;
44 //could memset to zero...
45 }
46
48 GReturn H_AddArray(ArrayNode* head, char* name, char* data)
49 {
50 ArrayNode* node; //the node to fill with data
51 if (head->count == 0) //no need to malloc, just fill the head
52 node = head;
53 else
54 {
55 node = malloc(sizeof(ArrayNode));
56 if (node) //malloc ok
57 H_InitArrayNode(node);
58 else
59 return G_BAD_FULL_MEMORY; //malloc failed
60 }
61
62 node->data = data; //copy pointer
63 strcpy(node->name, name); //copy name array
64 node->len = strlen(node->data); //output of GArrayUpload is null terminated.
65
66 head->count++; //count the node we just made
67 head->tail->next = node; //link the new node. If node == head this breaks the last-node-next-null guarantee
68 node->next = 0; //enforce the last-node-next-null guarantee
69 head->tail = node; //update head's tail pointer
70
71 return G_NO_ERROR;
72 }
73
75 void H_FreeArrays(ArrayNode* node)
76 {
77 if (node == 0) return; //recursive exit condition
78 free(node->data); //free this node's data
79 H_FreeArrays(node->next); //let downstream nodes recursvely free their data
80 free(node->next); //free the struct this node points to
81 //no need to free node, the head is declared on the stack
82 }
83
85 GReturn H_UploadArrayToList(GCon g, ArrayNode* head, char* name)
86 {
87 GReturn rc = G_NO_ERROR; //return code
88 char* array_buf; //buffer to hold array as it's uploaded
89 if (!(array_buf = malloc(MAXARRAY))) //allocate memory for single array upload
90 return G_BAD_FULL_MEMORY;
91
92 if ((rc = GArrayUpload(g, name, G_BOUNDS, G_BOUNDS, G_CR, array_buf, MAXARRAY)) != G_NO_ERROR) //get this array's data
93 return rc;
94
95 return H_AddArray(head, name, array_buf); //push the data into the array linked list
96 }
97
99 GReturn H_CreateArrayNode(ArrayNode* head, char* name)
100 {
101 char* array_buf; //buffer to hold array data
102 if (!(array_buf = malloc(MAXARRAY))) //allocate memory for single array upload
103 return G_BAD_FULL_MEMORY;
104 array_buf[0] = 0; //null terminate so len is correct when added
105 return H_AddArray(head, name, array_buf); //push the data into the array linked list
106 }
107
109 GReturn H_ArrayAddElement(ArrayNode* node, GCStringIn element)
110 {
111 int len = strlen(element);
112 if ((len + node->index + 1) >= MAXARRAY) //+1 for \r
113 return G_BAD_FULL_MEMORY;
114
115 strcpy(node->data + node->index, element); //copy the data to the array
116 node->index += len;
117 node->data[node->index++] = '\r'; //delim
118 node->data[node->index] = 0; //null terminate
119 node->len = node->index; //maintain len field
120 node->elements++; //count the element just added
121 return G_NO_ERROR;
122 }
123
125
132 GReturn H_DownloadArraysFromList(GCon g, ArrayNode* head)
133 {
134 ArrayNode* node = head;
135 GReturn rc = G_NO_ERROR;
136 char command[32]; //buffer for holding command calls
137 while ((node != 0) && (rc == G_NO_ERROR))
138 {
139
140 //*** Start array table modification
141 // Deallocate the array in case it's the wrong size.
142 sprintf(command, "DA %s[]", node->name);
143 if ((rc = GCmd(g, command)) != G_NO_ERROR)
144 return rc;
145
146 // Dimension the array with the correct length.
147 sprintf(command, "DM %s[%i]", node->name, node->elements);
148 if ((rc = GCmd(g, command)) != G_NO_ERROR)
149 return rc;
150 //*** End array table modification
151
152 rc = GArrayDownload(g, node->name, G_BOUNDS, G_BOUNDS, node->data); //download the array
153 node = node->next;
154 }
155 return rc;
156 }
157
159 GReturn H_WriteArrayCsv(ArrayNode* head, GCStringIn file_path)
160 {
161 if (head->count == 0) //nothing to do
162 return G_NO_ERROR;
163
164 FILE *file; //file pointer
165 size_t bytes; //length of data to write
166 size_t bytes_written; //bytes actually written to file
167 int colcount = 0; //column counter, used to prevent a trailing colon
168 int data_left = head->count; //counter for number of arrays that still have data left to be written
169 ArrayNode* node = head; //pointer to an array node in the list
170
171 if (!(file = fopen(file_path, "wb"))) //open file for writing, binary mode
172 return G_BAD_FILE;
173
174 //write the header
175 do
176 {
177 bytes = strlen(node->name);
178 bytes_written = fwrite(node->name, 1, bytes, file);
179 colcount++;
180
181 if (colcount != head->count) //write a comma if it's not the last column
182 {
183 bytes_written += fwrite(",", 1, 1, file);
184 bytes++;
185 }
186 else //write a carriage return
187 {
188 bytes_written += fwrite("\r", 1, 1, file);
189 bytes++;
190 }
191
192 if (bytes_written != bytes) //ensure we wrote what we wanted
193 {
194 fclose(file);
195 return G_BAD_FILE;
196 }
197 node = node->next;
198 } while (node != 0);
199
200
201 //now write the data
202 while (data_left) //continue writing rows as long as arrays have data to write
203 {
204 node = head;
205 colcount = 0;
206 do //write one row
207 {
208 bytes_written = 0;
209 bytes = 0;
210 if (node->index != node->len) //data available
211 {
212 while ((node->data[node->index] != '\r') //search for the carriage return delim
213 && (node->index < node->len)) //unless we reach the end
214 {
215 if (node->data[node->index] != ' ') //don't keep spaces
216 {
217 bytes_written += fwrite(node->data + node->index, 1, 1, file);
218 bytes++;
219 }
220 node->index++;
221 }
222
223 if (node->index == node->len) //reached the end of this data
224 data_left--; //decrement counter to indicate one less array with data to write
225
226 node->index++; //jump over \r delim
227 }
228
229 colcount++; //count the cell we just filled
230 if (colcount != head->count) //write a comma if it's not the last column
231 {
232 bytes_written += fwrite(",", 1, 1, file);
233 bytes++;
234 }
235 else //write a carriage return, even on the last line
236 {
237 bytes_written += fwrite("\r", 1, 1, file);
238 bytes++;
239 }
240
241 //check for write failure
242 if (bytes_written != bytes)
243 {
244 fclose(file);
245 return G_BAD_FILE;
246 }
247
248 node = node->next;
249 } while (node != 0);
250 } //while (data_left)
251
252 fclose(file);
253 return G_NO_ERROR;
254 }
255
256
257 GReturn GCALL GArrayDownloadFile(GCon g, GCStringIn file_path)
258 {
259 FILE *file;
260 GReturn rc = G_NO_ERROR;
261 char name[32]; //buffer for holding name of array
262 int n; //index into name
263 char element[32]; //buffer for holding ascii array element value
264 int e; //index into element
265 char c; //char currently being read
266
267 //Linked list to hold array data as it's organized for download
268 ArrayNode head; //first element (list head) lives on this stack
269 ArrayNode* node; //current node
270 H_InitArrayNode(&head);
271 head.tail = &head; //circular reference
272
273 if (!(file = fopen(file_path, "rb"))) //open file for reading, binary mode
274 return G_BAD_FILE;
275
276 //read out header, making a new ArrayNode for each
277 n = 0;
278 c = 0;
279 while (fread(&c, 1, 1, file))
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 break; //done reading headers
292 }
293 else
294 {
295 name[n++] = c;
296 }
297 }
298
299 //read each line of the file, pushing each cell into its corresponding ArrayNode
300 node = &head;
301 e = 0;
302 while (fread(&c, 1, 1, file))
303 {
304 if ((c == ',') || (c == '\r'))
305 {
306 if (e) //if anything read into element
307 {
308 element[e] = 0; //null terminate
309 H_ArrayAddElement(node, element);
310 e = 0; //start writing at start of element on next pass
311 }
312 node = node->next; //go to the next array
313 if (node == 0) node = &head; //wrap around to front
314 }
315 else
316 {
317 element[e++] = c;
318 }
319 }
320
321
322
323 fclose(file); //done with file
324
325 //By here, all the array data is in the linked-list starting at head, just download it
326 rc = H_DownloadArraysFromList(g, &head);
327 H_FreeArrays(&head); // don't forget to free memory
328 return rc;
329 }
330
331
332 GReturn GCALL GArrayUploadFile(GCon g, GCStringIn file_path, GCStringIn names)
333 {
334
335 GReturn rc = G_NO_ERROR; //return code
336 long bytes; //strlen placeholder
337 char array_names[1024]; //buffer to hold copy of names, or response to list arrays LA
338 char name[32]; //buffer to hold a single array name
339 int i, n; //indices
340 char c; //holder for the char currently being read
341 int bracket = 0; //increments when [ seen on a line, [ marks the end of the array name
342
343 //Linked list to hold array data
344 ArrayNode head; //first element (list head) lives on this stack
345 H_InitArrayNode(&head);
346 head.tail = &head; //circular reference
347
348 if (names == 0) //check for null pointer in arg
349 bytes = 0;
350 else
351 bytes = strlen(strcpy(array_names, names));
352
353 if (bytes == 0) //null or "", need to get the arrays from the controller
354 {
355 if ((rc = GCmdT(g, "LA", array_names, sizeof(array_names), 0)) != G_NO_ERROR) //Trimming command, get names from List Arrays (LA)
356 return rc; //no mallocs yet, so we can exit without free
357
358 bytes = strlen(array_names); //count the response
359 }
360
361 n = 0; //n is name[] index
362 for (i = 0; i < bytes; i++)
363 {
364 c = array_names[i];
365
366 if (c == '[')
367 bracket++; //[ marks the end of the array name
368
369 if ((c != ' ') && (c != '\r') && (c != '\n') && !bracket)
370 {
371 name[n++] = array_names[i]; //keep the char
372 }
373
374
375 if ((c == ' ') || (c == '\r') || (i == bytes - 1))
376 {
377 if (n) //if we have anything in name
378 {
379 name[n] = 0; // null terminate name
380 n = 0; // next time start filling name from start
381 bracket = 0; //forget any brackets we've seen
382
383 if ((rc = H_UploadArrayToList(g, &head, name)) != G_NO_ERROR) //Add data to list
384 {
385 H_FreeArrays(&head); // don't forget to free memory
386 return rc;
387 }
388 }
389 continue;
390 }
391
392 }
393
394 //By here, all the array data is in the linked-list starting at head.
395 rc = H_WriteArrayCsv(&head, file_path);
396 H_FreeArrays(&head); // don't forget to free memory
397 return rc;
398 }
H_ArrayData
Structure to create a linked list for array data.
Definition: arrays.c:16
H_AddArray
GReturn H_AddArray(ArrayNode *head, char *name, char *data)
Add an ArrayData node to the linked list.
Definition: arrays.c:48
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:90
H_InitArrayNode
void H_InitArrayNode(ArrayNode *node)
Function to initialize the memory of a new node.
Definition: arrays.c:34
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.
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)
Uplaods a particular array and adds it to the linked list.
Definition: arrays.c:85
GCon
void * GCon
Connection handle. Unique for each connection in process. Assigned a non-zero value in GOpen()...
Definition: gclib.h:65
H_FreeArrays
void H_FreeArrays(ArrayNode *node)
Frees all memory downsteam of node. After passing list head to this function, all memory is freed and...
Definition: arrays.c:75
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.
GReturn
int GReturn
Every function returns a value of type GReturn. See gclib_errors.h for possible values.
Definition: gclib.h:64
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:60
GCStringIn
const char * GCStringIn
C-string input to the library. Implies null-termination.
Definition: gclib.h:69
H_ArrayAddElement
GReturn H_ArrayAddElement(ArrayNode *node, GCStringIn element)
Adds an array element to an array node.
Definition: arrays.c:109
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:257
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:99
GArrayUploadFile
GReturn GCALL GArrayUploadFile(GCon g, GCStringIn file_path, GCStringIn names)
Array upload to file.
Definition: arrays.c:332
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:159
H_DownloadArraysFromList
GReturn H_DownloadArraysFromList(GCon g, ArrayNode *head)
Walks through the array linked list, downloading each.
Definition: arrays.c:132
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:84
G_BAD_FILE
#define G_BAD_FILE
Bad file path, bad file contents, or bad write.
Definition: gclib_errors.h:66
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