← Back to dashboard

File view

plc_485_comm.lib

1
//$Id: AUTOPAK_RUN_STATE.LIB,v 1.11 2003/10/31 23:42:21 Charles Hand Exp $
2
/*
3
  2012/06/20 Charles Hand
4
  Adapted COMM.LIB from STEPPER.LIB
5
  2005/08/23 Charles Hand
6
  New Library
7
*/
8
9
/*
10
***** Comm Port Pin Out *****
11
12
   SATA  		Cables		Printer		DB-9
13
14
7	GND		IS SHIELD			9
15
6	RS485 A 	IS GREEN			8	RS232 CTS
16
5	RS485 B 	IS WHITE			7
17
4	RS232 CTS   	PRINTER RED	Pin 3		6	RS485 B
18
3	RS232 RX  	PRINTER WHITE	Pin 2		5	GND
19
2	RS232 TX 	PRINTER GREEN	Pin 1		4
20
1	GND          	PRINTER BLACK	Pin 4		3  	RS232 TX
21
							2 	RS232 RX
22
                  					1	RS485 A
23
*/
24
25
/*** BeginHeader */
26
27
#ifndef __ISM_485_COMM_
28
#define __ISM_485_COMM_
29
30
// The node addresses
31
#define TCN_ADDRESS     1
32
#define GTLC1_ADDRESS   2
33
#define GTLC2_ADDRESS   3
34
#define RFS1_ADDRESS    4
35
36
// The "Request Status" command
37
enum {
38
	COMM_FUNCTION_REQUEST_STATUS = 1,
39
	COMM_FUNCTION_CV1_RETURN,
40
	COMM_FUNCTION_CV2_RETURN,
41
	COMM_FUNCTION_HOUR_TICK
42
};
43
44
#define DINBUFSIZE  255
45
#define DOUTBUFSIZE 255
46
47
/*** EndHeader */
48
49
/*** BeginHeader
50
51
     InitializeComm,
52
     CommAuth1,
53
     CommAuth2,
54
     CommAuth3,
55
     CommAuth4,
56
     CommBoiler,
57
     CommPolish,
58
     CommPHi,
59
     CommPMid,
60
     CommPLo,
61
     CommPump1,
62
     CommPump2,
63
     CommPump3,
64
     CommPump4,
65
     CommShunt,
66
     CommFOCV1,
67
     CommFOCV2,
68
     CommFOCV3,
69
     CommFOCV4,
70
     CommDrain,
71
     CommH2O,
72
     CommPolisherLevel,
73
     CommDayTankLevel,
74
     CommTest,
75
     CommEstop,
76
     CommAlarmLowPressure,
77
     CommAlarmMidPressure,
78
     CommAlarmNoFuelFlow,
79
     CommAlarmOperatorOverride,
80
     CommAlarmOperatorAborted,
81
     CommAlarmDeadTurbine,
82
     CommAlarmLowFuel,
83
     CommAlarmNoFuel,
84
     CommAlarmOverfill,
85
     CommAlarmSensorFailure,
86
     CommSupplyCv,
87
     CommReturnCv,
88
89
     CommRead,
90
     CommSendStatus,
91
     CommGetCV1Return,
92
     CommGetCV2Return,
93
     CommGetHourTick,
94
95
96
*/
97
98
//Global function prototypes **************************************************
99
100
void InitializeComm(int address);
101
int  CommRead();
102
void CommSendStatus ();
103
int CommGetCV1Return ();
104
int CommGetCV2Return ();
105
106
long CommGetHourTick ();
107
108
int CommAuth1 (int onOff);
109
int CommAuth2 (int onOff);
110
int CommAuth3 (int onOff);
111
int CommAuth4 (int onOff);
112
int CommBoiler(int onOff);
113
int CommPolish(int onOff);
114
int CommPHi   (int onOff);
115
int CommPMid  (int onOff);
116
int CommPLo   (int onOff);
117
int CommPump1 (int onOff);
118
int CommPump2 (int onOff);
119
int CommPump3 (int onOff);
120
int CommPump4 (int onOff);
121
int CommShunt (int onOff);
122
int CommFOCV1 (int onOff);
123
int CommFOCV2 (int onOff);
124
int CommFOCV3 (int onOff);
125
int CommFOCV4 (int onOff);
126
int CommDrain (int onOff);
127
int CommH2O   (int onOff);
128
int CommEstop (int onOff);
129
int CommPolisherLevel(int level);
130
int CommDayTankLevel(int level);
131
int CommCVReturn (int onOff);
132
133
int CommAlarmLowPressure(int onOff);
134
int CommAlarmMidPressure(int onOff);
135
int CommAlarmNoFuelFlow(int onOff);
136
int CommAlarmOperatorOverride(int onOff);
137
int CommAlarmOperatorAborted(int onOff);
138
int CommAlarmDeadTurbine(int onOff);
139
int CommAlarmLowFuel(int onOff);
140
int CommAlarmNoFuel(int onOff);
141
int CommAlarmOverfill(int onOff);
142
int CommAlarmSensorFailure(int onOff);
143
int CommTest(int type, char result, float rate, int indexToNoteTable);
144
145
int CommSupplyCv(int onOff);
146
int CommReturnCv(int onOff);
147
148
/*** EndHeader */
149
150
//Local function prototypes *********************************************
151
152
void sendMessage(char *aMessage);
153
void addIntString(char **ptr, int value, int *spaceLeft, int nDigits);
154
void addIntStringParameter(char **ptr, int statusCode, int value, int *spaceLeft);
155
156
//Constants *******************************************************
157
158
#define COMM_BAUD_RATE 38400L
159
#define RS485_TX_ENABLE_BIT  2
160
161
// The send buffer size depends on the maximum number of values
162
//   that have to be sent. Well, there are 38 values defined now,
163
//   of course no single node would ever be sending all the values.
164
//   You need six bytes for each value, plus a few bytes of overhead
165
//   for the message. Let's pick a ridiculously large number 300
166
#define SEND_BUFFER_LENGTH 300
167
#define RECEIVE_PAYLOAD_LENGTH 300
168
#define RECEIVE_MESSAGE_MAX_LENGTH (RECEIVE_PAYLOAD_LENGTH + 3)
169
170
// Our request messages look like this:
171
// #aa|ff[|nn|vv...];
172
// Where aa is the node address, ff is the function,
173
//   nn is a variable name and vv is the variable value.
174
// It's all in ascii and it's all in decimal.
175
// For now all fields are exactly two characters long, permitting a maximum value of 99
176
// Our response messages look like this:
177
// @aa|ff[|nn|vv...];
178
//   Same as the request message except the first character is
179
//   '@' instead of '#'
180
181
// Our messages begin with ? and end with !
182
// Fields are delimited with |
183
#define MESSAGE_REQUEST_BEGIN_TOKEN '#'
184
#define MESSAGE_RESPONSE_BEGIN_TOKEN '@'
185
#define MESSAGE_DELIMITER '|'
186
#define MESSAGE_END_TOKEN ';'
187
188
// The fields in our messages are identified using these macros
189
enum {
190
	COMM_STATUS_AUTH1 = 1,
191
			COMM_STATUS_AUTH2,
192
			COMM_STATUS_AUTH3,
193
			COMM_STATUS_AUTH4,
194
			COMM_STATUS_BOILER,
195
			COMM_STATUS_POLISH,
196
			COMM_STATUS_PHI,
197
			COMM_STATUS_PMID,
198
			COMM_STATUS_PLO,
199
    COMM_STATUS_PUMP1, // 10
200
			COMM_STATUS_PUMP2,
201
			COMM_STATUS_PUMP3,
202
			COMM_STATUS_PUMP4,
203
			COMM_STATUS_SHUNT,
204
			COMM_STATUS_FOCV1,
205
			COMM_STATUS_FOCV2,
206
			COMM_STATUS_FOCV3,
207
			COMM_STATUS_FOCV4,
208
			COMM_STATUS_DRAIN,
209
    COMM_STATUS_H2O, // 20
210
			COMM_STATUS_POLISHER_LEVEL,
211
			COMM_STATUS_DAYTANK_LEVEL,
212
			COMM_TEST_SERIAL_NUMBER,
213
			COMM_TEST_RESULT,
214
			COMM_TEST_TYPE,
215
			COMM_TEST_RATE,
216
			COMM_TEST_INDEX,
217
			COMM_STATUS_ESTOP,
218
			COMM_CV_RETURN,
219
			COMM_ALARM_LOW_PRESSURE,
220
    COMM_ALARM_MID_PRESSURE, // 30
221
			COMM_ALARM_NO_FUEL_FLOW,
222
			COMM_ALARM_OPERATOR_OVERRIDE,
223
			COMM_ALARM_OPERATOR_ABORTED,
224
			COMM_ALARM_LOW_FUEL,
225
			COMM_ALARM_NO_FUEL,
226
			COMM_ALARM_OVERFILL,
227
            COMM_ALARM_SENSOR_FAILURE,
228
            COMM_STATUS_SUPPLY_CV,
229
            COMM_STATUS_RETURN_CV,
230
    COMM_FIRST_POLL, //40
231
    		COMM_ALARM_DEAD_TURBINE
232
         } ;
233
234
//#define EVENT_QUEUE_LENGTH 10
235
#define DEFAULT_INT -32767
236
237
238
/*** Local variables ***/
239
240
// These are all the values that may get communicated to the display
241
242
int auth1;
243
int auth2;
244
int auth3;
245
int auth4;
246
int boiler;
247
int polish;
248
int phi;
249
int pmid;
250
int plo;
251
int pump1;
252
int pump2;
253
int pump3;
254
int pump4;
255
int shunt;
256
int focv1;
257
int focv2;
258
int focv3;
259
int focv4;
260
int drain;
261
int alarm;
262
int h2o;
263
int estop;
264
int polisherLevel;
265
int dayTankLevel;
266
int cvReturn;
267
int alarmLowPressure;
268
int alarmMidPressure;
269
int alarmNoFuelFlow;
270
int alarmOperatorOverride;
271
int alarmOperatorAbort;
272
int alarmDeadTurbine;
273
int alarmLowFuel;
274
int alarmNoFuel;
275
int alarmOverfill;
276
int alarmSensorFailure;
277
int testType;
278
int testResult;
279
int testRate;
280
int testIndex;
281
int supplyCv;
282
int returnCv;
283
284
int functionCV1Return;
285
int functionCV2Return;
286
long functionHourTick;
287
288
int  commRxFunction;
289
char commRxMessage[RECEIVE_MESSAGE_MAX_LENGTH];
290
char commRxMessageTemp[RECEIVE_MESSAGE_MAX_LENGTH];
291
int  commRxIdx;
292
char myAddress;              // The unique address of this node
293
int currentTestSerialNumber;
294
int firstPoll;
295
296
297
//Code  ************************************************************************
298
299
// Initialize the library.
300
// address is the address of this node
301
// Make sure the transmit bit is high
302
// Set the baud rate to COMM_BAUD_RATE
303
304
void InitializeComm (int address)
305
{
306
    // All these status variables are initialized to -1.
307
    // If they are never set to any other value, they will never be transmitted
308
    //   as part of the status response message.
309
    // That way, TCN_COMM is common to all the nodes, but only the signals
310
    //   specificically given a value by the node will be sent
311
312
    auth1  = DEFAULT_INT;
313
    auth2  = DEFAULT_INT;
314
    auth3  = DEFAULT_INT;
315
    auth4  = DEFAULT_INT;
316
    boiler = DEFAULT_INT;
317
    polish = DEFAULT_INT;
318
    phi    = DEFAULT_INT;
319
    pmid   = DEFAULT_INT;
320
    plo    = DEFAULT_INT;
321
    pump1  = DEFAULT_INT;
322
    pump2  = DEFAULT_INT;
323
    pump3  = DEFAULT_INT;
324
    pump4  = DEFAULT_INT;
325
    shunt  = DEFAULT_INT;
326
    focv1  = DEFAULT_INT;
327
    focv2  = DEFAULT_INT;
328
    focv3  = DEFAULT_INT;
329
    focv4  = DEFAULT_INT;
330
    drain  = DEFAULT_INT;
331
    alarm  = DEFAULT_INT;
332
    h2o    = DEFAULT_INT;
333
    estop  = DEFAULT_INT;
334
    polisherLevel = DEFAULT_INT;
335
    dayTankLevel = DEFAULT_INT;
336
    cvReturn = DEFAULT_INT;
337
    alarmLowPressure = DEFAULT_INT;
338
    alarmMidPressure = DEFAULT_INT;
339
    alarmNoFuelFlow = DEFAULT_INT;
340
    alarmOperatorOverride = DEFAULT_INT;
341
    alarmOperatorAbort = DEFAULT_INT;
342
    alarmDeadTurbine = DEFAULT_INT;
343
    alarmLowFuel = DEFAULT_INT;
344
    alarmNoFuel = DEFAULT_INT;
345
    alarmOverfill = DEFAULT_INT;
346
    alarmSensorFailure = DEFAULT_INT;
347
    testType = DEFAULT_INT;
348
    testResult = DEFAULT_INT;
349
    testRate = DEFAULT_INT;
350
    testIndex = DEFAULT_INT;
351
    supplyCv = DEFAULT_INT;
352
    returnCv = DEFAULT_INT;
353
354
    currentTestSerialNumber = 0;
355
    firstPoll = 1;
356
    myAddress = (char)address;
357
    commRxIdx = 0;
358
    memset(commRxMessage, 0, sizeof(commRxMessage));
359
    BitWrPortI(PCDR, &PCDRShadow, 0, RS485_TX_ENABLE_BIT);
360
361
    BitWrPortI (PCFR, &PCFRShadow, 1, 0); // Configure Port C bit 0
362
    //  as Serial Port D outupt
363
364
    WrPortI (SDCR, &SDCRShadow, 0x00);    // Set serial port D
365
    // to use port C for serial input,
366
    // async mode, 8 bits, no interrupt
367
368
    serDopen(COMM_BAUD_RATE);
369
}
370
371
int CommAuth1(int onOff)
372
{
373
    auth1 = onOff;
374
}
375
int CommAuth2(int onOff)
376
{
377
    auth2 = onOff;
378
}
379
int CommAuth3(int onOff)
380
{
381
    auth3 = onOff;
382
}
383
int CommAuth4(int onOff)
384
{
385
    auth4 = onOff;
386
}
387
int CommBoiler(int onOff)
388
{
389
    boiler = onOff;
390
}
391
int CommPolish (int onOff)
392
{
393
	 polish = onOff;
394
}
395
int CommPHi(int onOff)
396
{
397
    phi = onOff;
398
}
399
int CommPMid (int onOff)
400
{
401
    pmid = onOff;
402
}
403
int CommPLo(int onOff)
404
{
405
    plo = onOff;
406
}
407
int CommPump1(int onOff)
408
{
409
    pump1 = onOff;
410
}
411
int CommPump2(int onOff)
412
{
413
    pump2 = onOff;
414
}
415
int CommPump3(int onOff)
416
{
417
    pump3 = onOff;
418
}
419
int CommPump4(int onOff)
420
{
421
    pump4 = onOff;
422
}
423
int CommShunt(int onOff)
424
{
425
    shunt = onOff;
426
}
427
int CommFOCV1(int onOff)
428
{
429
    focv1 = onOff;
430
}
431
int CommFOCV2(int onOff)
432
{
433
    focv2 = onOff;
434
}
435
int CommFOCV3(int onOff)
436
{
437
    focv3 = onOff;
438
}
439
int CommFOCV4(int onOff)
440
{
441
    focv4 = onOff;
442
}
443
int CommDrain(int onOff)
444
{
445
    drain = onOff;
446
}
447
int CommH2O(int onOff)
448
{
449
    h2o = onOff;
450
}
451
int CommEstop(int onOff)
452
{
453
    estop = onOff;
454
}
455
int CommPolisherLevel(int level)
456
{
457
    polisherLevel = level;
458
}
459
int CommDayTankLevel(int level)
460
{
461
    dayTankLevel = level;
462
}
463
int CommCVReturn(int onOff)
464
{
465
    cvReturn = onOff;
466
}
467
int CommSupplyCv(int onOff)
468
{
469
    supplyCv = onOff;
470
}
471
int CommReturnCv(int onOff)
472
{
473
    returnCv = onOff;
474
}
475
476
int CommAlarmLowPressure(int onOff)
477
{
478
    alarmLowPressure = onOff;
479
}
480
481
int CommAlarmMidPressure(int onOff)
482
{
483
    alarmMidPressure = onOff;
484
}
485
486
int CommAlarmNoFuelFlow(int onOff)
487
{
488
    alarmNoFuelFlow = onOff;
489
}
490
491
int CommAlarmOperatorOverride(int onOff)
492
{
493
    alarmOperatorOverride = onOff;
494
}
495
496
int CommAlarmOperatorAbort(int onOff)
497
{
498
    alarmOperatorAbort = onOff;
499
}
500
501
int CommAlarmDeadTurbine(int onOff)
502
{
503
	alarmDeadTurbine = onOff;
504
}
505
506
int CommAlarmLowFuel(int onOff)
507
{
508
    alarmLowFuel = onOff;
509
}
510
511
int CommAlarmNoFuel(int onOff)
512
{
513
    alarmNoFuel = onOff;
514
}
515
516
int CommAlarmOverfill(int onOff)
517
{
518
    alarmOverfill = onOff;
519
}
520
521
int CommAlarmSensorFailure(int onOff)
522
{
523
    alarmSensorFailure = onOff;
524
}
525
526
int CommTest(int type, char result, float rate, int indexToNoteTable)
527
{
528
	firstPoll = 0;
529
    currentTestSerialNumber++;
530
    currentTestSerialNumber &= 0xffff;
531
    testType = type;
532
    testResult = result == 'p' ? 1 : 0;
533
    testRate = (int)(rate * 1000.0 + 0.5);
534
    testIndex = indexToNoteTable;
535
536
    #if ADDRESS == TCN_ADDRESS
537
		UpdateLog(TEST_LOG, testType, testResult, testRate, testIndex, 1, 1);
538
    #endif
539
540
}
541
542
// Copy the serial port D contents to our local buffer, and see if there is
543
// a message in there. If there is a message, and it is for us, return the
544
// function code. Otherwise, return zero.
545
546
int CommRead()
547
{
548
    int nChars;
549
    char *msgStart;
550
    char *msgEnd;
551
    char conv[5];
552
    int anAdr;
553
    int aRxChar;
554
    int result;
555
    char *p1,*p2;
556
557
    msgStart = NULL;
558
    msgEnd = NULL;
559
560
    result = 0;
561
    memset(conv,0,sizeof(conv));
562
563
    // How many characters are there in the Serial Port D buffer?
564
    nChars = serDrdUsed();
565
566
    // OK, read that many characters and add them to the local buffer, commRxMessage
567
    //   but don't go past the end of the buffer (leaving room for a null terminator)
568
    while (nChars-- > 0 && commRxIdx < sizeof(commRxMessage) - 1) {
569
        aRxChar = (char)serDgetc();
570
        commRxMessage[commRxIdx++] = aRxChar;
571
        commRxMessage[commRxIdx] = 0; // Whenever we add a character, add a null terminator
572
    }
573
574
    // Search the entire local buffer backwards for a message end token
575
    // Discard messages which aren't for us, and discard echos of our
576
    //   own outbound messages.
577
    // Begin by detecting the oldest end token in the buffer
578
    while (result == 0 && (msgEnd = strchr(commRxMessage, MESSAGE_END_TOKEN)) != NULL) {
579
580
        // Now that we have found an end token,
581
        //   there ought to be a begin token before the end token
582
        //   If not, our communications are being scrambled somehow
583
		msgStart = msgEnd;
584
		while (msgStart != commRxMessage &&
585
               *msgStart != MESSAGE_RESPONSE_BEGIN_TOKEN &&
586
               *msgStart != MESSAGE_REQUEST_BEGIN_TOKEN)
587
        {
588
            msgStart--;
589
        }
590
591
      if (*msgStart == MESSAGE_REQUEST_BEGIN_TOKEN) {
592
      	memset(conv, 0, sizeof(conv));
593
         conv[0] = msgStart[1];
594
         conv[1] = msgStart[2];
595
         anAdr = atoi(conv);
596
         if (anAdr == myAddress) {
597
           	 memset(conv, 0, sizeof(conv));
598
             conv[0] = msgStart[4];
599
             conv[1] = msgStart[5];
600
             result = atoi(conv);
601
         }
602
         if (result == COMM_FUNCTION_CV1_RETURN) {
603
            memset(conv, 0, sizeof(conv));
604
            conv[0] = msgStart[7];
605
            conv[1] = msgStart[8];
606
            functionCV1Return = atoi(conv);
607
         }
608
         if (result == COMM_FUNCTION_CV2_RETURN) {
609
            memset(conv, 0, sizeof(conv));
610
            conv[0] = msgStart[7];
611
            conv[1] = msgStart[8];
612
            functionCV2Return = atoi(conv);
613
         }
614
        if (result == COMM_FUNCTION_HOUR_TICK) {
615
            memset(conv, 0, sizeof(conv));
616
            functionHourTick = atol(conv);
617
         }
618
      }
619
620
621
        // Now any time we find an end token,
622
        //   we will adjust the local buffer by shifting everything starting
623
        //   with the character after the end token down to the beginning
624
        //   of the local buffer, and setting the commRxIdx accordingly
625
626
        strcpy(commRxMessageTemp, msgEnd+1);
627
        memset(commRxMessage, 0, sizeof(commRxMessage));
628
        strcpy(commRxMessage, commRxMessageTemp);
629
        commRxIdx = strlen(commRxMessage);
630
    }
631
    if (msgEnd == NULL && commRxIdx >= sizeof(commRxMessage) - 1) {
632
        // This is very bad. Our entire local buffer is full, yet we don't find
633
        //   a single message end token in it. Well, might as well dump the
634
        //   buffer in hopes that good stuff may yet into the serial port
635
        memset(commRxMessage, 0, sizeof(commRxMessage));
636
        commRxIdx = 0;
637
    }
638
    return result;
639
}
640
641
int CommGetCV1Return() {
642
	char message[SEND_BUFFER_LENGTH];
643
   char *ptr;
644
   int spaceLeft;
645
646
   ptr = message;
647
   *ptr++ = MESSAGE_RESPONSE_BEGIN_TOKEN;
648
   spaceLeft = SEND_BUFFER_LENGTH - 2;
649
   addIntString(&ptr, myAddress, &spaceLeft, 2);
650
   addIntString(&ptr, COMM_FUNCTION_CV1_RETURN, &spaceLeft, 2);
651
	if (spaceLeft >= 0) // actually need one, but final pipe character will be overwritten
652
   {
653
        ptr--; // Overwrite the last pipe character
654
        *ptr++ = MESSAGE_END_TOKEN;
655
        *ptr = 0;
656
        sendMessage(message);
657
   }
658
	return functionCV1Return;
659
}
660
661
int CommGetCV2Return() {
662
	char message[SEND_BUFFER_LENGTH];
663
   char *ptr;
664
   int spaceLeft;
665
666
   ptr = message;
667
   *ptr++ = MESSAGE_RESPONSE_BEGIN_TOKEN;
668
   spaceLeft = SEND_BUFFER_LENGTH - 2;
669
   addIntString(&ptr, myAddress, &spaceLeft, 2);
670
   addIntString(&ptr, COMM_FUNCTION_CV2_RETURN, &spaceLeft, 2);
671
	if (spaceLeft >= 0) // actually need one, but final pipe character will be overwritten
672
   {
673
        ptr--; // Overwrite the last pipe character
674
        *ptr++ = MESSAGE_END_TOKEN;
675
        *ptr = 0;
676
        sendMessage(message);
677
   }
678
	return functionCV2Return;
679
}
680
681
long CommGetHourTick() {
682
	char message[SEND_BUFFER_LENGTH];
683
   char *ptr;
684
   int spaceLeft;
685
686
   ptr = message;
687
   *ptr++ = MESSAGE_RESPONSE_BEGIN_TOKEN;
688
   spaceLeft = SEND_BUFFER_LENGTH - 2;
689
   addIntString(&ptr, myAddress, &spaceLeft, 2);
690
   addIntString(&ptr, COMM_FUNCTION_HOUR_TICK, &spaceLeft, 2);
691
	if (spaceLeft >= 0) // actually need one, but final pipe character will be overwritten
692
   {
693
        ptr--; // Overwrite the last pipe character
694
        *ptr++ = MESSAGE_END_TOKEN;
695
        *ptr = 0;
696
        sendMessage(message);
697
   }
698
	return functionHourTick;
699
}
700
701
// This should be called in response to a
702
//   COMM_FUNCTION_REQUEST_STATUS from the master
703
void CommSendStatus()
704
{
705
    char message[SEND_BUFFER_LENGTH]; // A temporary buffer for our message
706
    char *ptr;
707
    int spaceLeft;
708
    int i, intRate;
709
710
    ptr = message;
711
    *ptr++ = MESSAGE_RESPONSE_BEGIN_TOKEN;
712
    spaceLeft = SEND_BUFFER_LENGTH - 2; // minus one for null terminator, minus one for begin token
713
    addIntString(&ptr, myAddress, &spaceLeft, 2);
714
    addIntString(&ptr, COMM_FUNCTION_REQUEST_STATUS, &spaceLeft, 2);
715
    if (firstPoll)
716
    {
717
		addIntStringParameter(&ptr, COMM_FIRST_POLL, 0, &spaceLeft);
718
	}
719
    addIntStringParameter(&ptr, COMM_STATUS_AUTH1, auth1, &spaceLeft);
720
    addIntStringParameter(&ptr, COMM_STATUS_AUTH2, auth2, &spaceLeft);
721
    addIntStringParameter(&ptr, COMM_STATUS_AUTH3, auth3, &spaceLeft);
722
    addIntStringParameter(&ptr, COMM_STATUS_AUTH4, auth4, &spaceLeft);
723
    addIntStringParameter(&ptr, COMM_STATUS_BOILER, boiler, &spaceLeft);
724
    addIntStringParameter(&ptr, COMM_STATUS_POLISH, polish, &spaceLeft);
725
    addIntStringParameter(&ptr, COMM_STATUS_PHI, phi, &spaceLeft);
726
    addIntStringParameter(&ptr, COMM_STATUS_PMID, pmid, &spaceLeft);
727
    addIntStringParameter(&ptr, COMM_STATUS_PLO, plo, &spaceLeft);
728
    addIntStringParameter(&ptr, COMM_STATUS_PUMP1, pump1, &spaceLeft);
729
    addIntStringParameter(&ptr, COMM_STATUS_PUMP2, pump2, &spaceLeft);
730
    addIntStringParameter(&ptr, COMM_STATUS_PUMP3, pump3, &spaceLeft);
731
    addIntStringParameter(&ptr, COMM_STATUS_PUMP4, pump4, &spaceLeft);
732
    addIntStringParameter(&ptr, COMM_STATUS_SHUNT, shunt, &spaceLeft);
733
    addIntStringParameter(&ptr, COMM_STATUS_FOCV1, focv1, &spaceLeft);
734
    addIntStringParameter(&ptr, COMM_STATUS_FOCV2, focv2, &spaceLeft);
735
    addIntStringParameter(&ptr, COMM_STATUS_FOCV3, focv3, &spaceLeft);
736
    addIntStringParameter(&ptr, COMM_STATUS_FOCV4, focv4, &spaceLeft);
737
    addIntStringParameter(&ptr, COMM_STATUS_DRAIN, drain, &spaceLeft);
738
    addIntStringParameter(&ptr, COMM_STATUS_H2O, h2o, &spaceLeft);
739
    addIntStringParameter(&ptr, COMM_STATUS_ESTOP, estop, &spaceLeft);
740
    addIntStringParameter(&ptr, COMM_STATUS_POLISHER_LEVEL, polisherLevel, &spaceLeft);
741
    addIntStringParameter(&ptr, COMM_STATUS_DAYTANK_LEVEL, dayTankLevel, &spaceLeft);
742
    addIntStringParameter(&ptr, COMM_CV_RETURN, cvReturn, &spaceLeft);
743
    addIntStringParameter(&ptr, COMM_STATUS_SUPPLY_CV, supplyCv, &spaceLeft);
744
    addIntStringParameter(&ptr, COMM_STATUS_RETURN_CV, returnCv, &spaceLeft);
745
    addIntStringParameter(&ptr, COMM_ALARM_LOW_PRESSURE, alarmLowPressure, &spaceLeft);
746
    addIntStringParameter(&ptr, COMM_ALARM_MID_PRESSURE, alarmMidPressure, &spaceLeft);
747
    addIntStringParameter(&ptr, COMM_ALARM_NO_FUEL_FLOW, alarmNoFuelFlow, &spaceLeft);
748
    addIntStringParameter(&ptr, COMM_ALARM_OPERATOR_OVERRIDE, alarmOperatorOverride, &spaceLeft);
749
    addIntStringParameter(&ptr, COMM_ALARM_OPERATOR_ABORTED, alarmOperatorAbort, &spaceLeft);
750
    addIntStringParameter(&ptr, COMM_ALARM_DEAD_TURBINE, alarmDeadTurbine, &spaceLeft);
751
    addIntStringParameter(&ptr, COMM_ALARM_LOW_FUEL, alarmLowFuel, &spaceLeft);
752
    addIntStringParameter(&ptr, COMM_ALARM_NO_FUEL, alarmNoFuel, &spaceLeft);
753
    addIntStringParameter(&ptr, COMM_ALARM_OVERFILL, alarmOverfill, &spaceLeft);
754
    addIntStringParameter(&ptr, COMM_ALARM_SENSOR_FAILURE, alarmSensorFailure, &spaceLeft);
755
756
    if (testType != DEFAULT_INT)
757
    {
758
        addIntStringParameter(&ptr, COMM_TEST_SERIAL_NUMBER, currentTestSerialNumber, &spaceLeft);
759
        addIntStringParameter(&ptr, COMM_TEST_RESULT, testResult, &spaceLeft);
760
        addIntStringParameter(&ptr, COMM_TEST_TYPE, testType, &spaceLeft);
761
        addIntStringParameter(&ptr, COMM_TEST_RATE, testRate, &spaceLeft);
762
        addIntStringParameter(&ptr, COMM_TEST_INDEX, testIndex, &spaceLeft);
763
    }
764
765
    if (spaceLeft >= 0) // actually need one, but final pipe character will be overwritten
766
    {
767
        ptr--; // Overwrite the last pipe character
768
        *ptr++ = MESSAGE_END_TOKEN;
769
        *ptr = 0;
770
        sendMessage(message);
771
    }
772
}
773
774
void sendMessage(char *message) {
775
    long timeNow;
776
    BitWrPortI(PCDR, &PCDRShadow, 1, RS485_TX_ENABLE_BIT);
777
    serDwrite(message, strlen(message));
778
    while (serDwrUsed() > 0) {}
779
    // Assuming we're at 38400 buad, the last character will take
780
    //   26 microseconds to send.
781
    // The smallest time we can reliably delay is 1 ms by
782
    //   trying to delay 2 ms
783
    // And we can't use MsDelay on account of library ordering
784
    timeNow = MS_TIMER;
785
    while (MS_TIMER - timeNow <= 2) {}
786
    BitWrPortI(PCDR, &PCDRShadow, 0, RS485_TX_ENABLE_BIT);
787
}
788
789
790
void addIntStringParameter(char **ptr, int statusCode, int value, int *spaceLeft)
791
{
792
    if (value == DEFAULT_INT) return;
793
    addIntString(ptr, statusCode, spaceLeft, 2);
794
    addIntString(ptr, value, spaceLeft, 0);
795
}
796
797
// Write the string representation of an integer to a char pointer.
798
// spaceLeft is the space left in the buffer (not counting null terminator)
799
// If the string-converted integer takes more space than spaceLeft, then
800
//   set spaceLeft to -1. (This function will do nothing when spaceLeft is < 0)
801
// We always append a MESSAGE_DELIMITER. When the entire message has been
802
//   built, the final MESSAGE_DELIMITER will be deleted.
803
void addIntString(char **ptr, int value, int *spaceLeft, int nDigits)
804
{
805
    char valueString[10]; // bigger than number of characters in max integer
806
    int max;
807
    int len;
808
    char formatString[10]; // This is a little more than needed
809
810
    memset(formatString, 0, sizeof(formatString));
811
    if (nDigits > 0)
812
    {
813
        if (nDigits > 6) nDigits = 6;
814
        sprintf(formatString, "%%0%dd", nDigits); // e.g. "%02d"
815
    } else
816
    {
817
        strcpy(formatString, "%d");
818
    }
819
    formatString[strlen(formatString)] = MESSAGE_DELIMITER;
820
821
    max = sizeof(valueString)-1;
822
    len = snprintf(valueString, max, formatString, value); // Print number followed by pipe character
823
    if (len <= max && len <= *spaceLeft)
824
    {
825
        *spaceLeft -= len;
826
        strcpy(*ptr, valueString);
827
        *ptr += len;
828
    }
829
    else if (len > *spaceLeft)
830
    {
831
        *spaceLeft = -1;
832
    }
833
}
834
835
836
/*** BeginHeader */
837
838
839
#endif
840
841
/*** EndHeader */