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