øõTRiLOGI Ver 5.0 0,T95Input 1,T97Input 2,BundInput 4,FuelPulse1 5,FuelPulse2 ~ 0,T95Alarm 1,T97Alarm 2,BundAlarm 3,SensorFailure 4,DeliveryAlarm 6,FuelValvePWM 7,FPulse ~ 0,ValidFillReq 16,BufferFull 17,ScanDelay 19,T1SensorFail 20,T2SensorFail 21,T3SensorFail 22,T4SensorFail 23,T5SensorFail 24,T6SensorFail 26,T1Level95 27,T2Level95 28,T3Level95 29,T4Level95 30,T5Level95 31,T6Level95 33,T1Level97 34,T2Level97 35,T3Level97 36,T4Level97 37,T5Level97 38,T6Level97 40,FillEvent 42,ValidFillEvent 43,InvalidFillEvent ~ ~ 0,Seq1 32 1,Seq2 32 2,Seq3 32 3,Seq4 32 4,Seq5 32 5,Seq6 32 6,Seq7 32 7,Seq8 32 8,Cnt60 60 10,ValidFillCntr 60 ~ $DSGaging_07 - 6/18/2015 Added Bund Alarm INPUTþ Now back to handling only 2 fuel island pulse streams.þ RELAYS added for each tank for 95% and 97% volume levels. These RELAYS areþ SET/CLEARED via custom functions. These RELAYS are OR'd in with the þ thermistor based level sensing system to produce a combined OUTPUT.þþ Changes to tank fill detection:þ 1. Now using a single circular queue for the entire system rather than a queueþ for each tank.þ 2. Queue size is now 10.þ 3. Work in progress to get a cue from the HMI has to when a tanker has properly logged inþ a delivery. This cue will be used to "tell" a authorized fuel delivery from an unauthorizedþ delivery.þ $DSGaging_06 - 6/12/2015 Adjustments to how system status is maintained.þ The actual tank system has a high and very f'n high alarms in each tank.þ The alarms are all or'd together and presented as two separate inputsþ to the PLC. As a result, the PLC does not "know" which tank is overþ filled.þþ There is now the possibility of 4 pulse streams for fuel island pumps.þ Previously there were only 2 fuel pulse sources.þ $DSGaging_05 - 5/28/2015 Clean up for road trip to Englandþ Removed the use of an upcounter for managing a circular fifo pointer. I decidedþ that this approach was show-boating and made the code harder to understand.þ $DSGaging_04 - 4/28/2015 First demo code shipped to Bob Evasonþ This version is working with the HMI project "MERTON GARAGE gsd02"þ $DSGaging_03 - change adc and filter to simualte 13 bit converter 0..8192þ Work on tank status information for the HMIþ $DSGaging_02 - work on circular buffer for dvdt algorithm.þ the current approach is to sample the current tank volume once every 60 seconds.þ This sample is intered into a queue and will reappear 10 minutes later.þ The sampple pulled from the queue is evaluated against the current tank levelþ and this diference is reported the dvdt value (rate of change)þ $4/21/15 bug with mm/ma equation m/b values are wrong!þ $DSGaging_01.pc6 4/17/2015 Gary Dickinsonþ 1st pass at demo code for gaging systemþ $**********************************************************************þþThis section handles system initializationþþ**********************************************************************þ %LInit=1st.Scan $********************************************************************************************þForeground Task (Main Loop State Machine)þþ The foreground task:þ 1. Samples ADC(x) inputs once per second.þ 2. Computes Volumes and ullages on each passþ 3. Calculates rate of range (dvdt) once each minute (60 passes)þ 4. Updates system status once each pass.þ þ The following rungs make up the state transistion rulesþþ********************************************************************************************þþ %JSeq1:5=(Seq1:2*/Cnt60+Seq1:4) %JSeq1:4=Seq1:3 %JSeq1:3=Seq1:2*Cnt60 %JSeq1:2=Seq1:1*Clk:1.0s %JSeq1:1=Seq1:0*/Clk:1.0s %JSeq1:0=(1st.Scan+Seq1:5*ScanDelay) $********************************************************************************************þForeground Task hardware actions taken at each stateþþCnt60 is a down COUNTER that counts loops though the foreground task statemachine.þOnce every 60 seconds, the contact associated with this COUNTER goes active and thisþallows the state machine to enter state 1:3 to calculate the tank level rate of changeþ(dvdt).þþThe ScanDelay relay is active on state 1:4 to assure that the state manchine will not move þfrom 1:4 -> 1:5 -> 1:0 on a single scan of the ladder logic.þþ********************************************************************************************þ %1Cnt60=Seq1:3 Cnt60=Seq1:1 ScanDelay=Seq1:5 $********************************************************************************************þForeground Task custom functionsþþ********************************************************************************************þ %LReadADCs=Seq1:2 %LCalcDvDt=Seq1:3 %LNewDataTable=Seq1:4 %LUpdateStatus=Seq1:5 $********************************************************************************************þEnd of Foreground Task State Machine Codeþþ********************************************************************************************þ $********************************************************************************************þþLadder logic to support tank alarmsþþ********************************************************************************************þ T95Alarm=(T95Input+(T1Level95+(T2Level95+(T3Level95+(T4Level95+(T5Level95+T6Level95)))))) T97Alarm=(T97Input+(T1Level97+(T2Level97+(T3Level97+(T4Level97+(T5Level97+T6Level97)))))) BundAlarm=BundInput SensorFailure=(T1SensorFail+(T2SensorFail+(T3SensorFail+(T4SensorFail+(T5SensorFail+T6SensorFail))))) $********************************************************************************************þþLadder logic to detect authorized vs. unauthorized tank filling eventsþþThe RELAY, ValidFillReq, is set by the HMI to indicate that a authorized tank fillingþoperation has been requested. ValidFillReq is cleared after a time period determinedþby ValidFillCntr.þþThe RELAY, FillEvent, is managed by by a PLC custom function. This RELAY is Set whenþthe system volume is increasing.þþþ********************************************************************************************þ %1ValidFillCntr=/ValidFillReq %DValidFillReq=ValidFillCntr ValidFillCntr=Clk:1min ValidFillEvent=FillEvent*ValidFillReq InvalidFillEvent=FillEvent*/ValidFillReq DeliveryAlarm=InvalidFillEvent $********************************************************************************************þLadder logic to track input pulses from the fuel islandþþ********************************************************************************************þ %LCountFP1=FuelPulse1 %LCountFP2=FuelPulse2 $********************************************************************************************þDebug code ...þ********************************************************************************************þ $Pulse source that can be jumped back into the FuelPUlse(1..4) inputs forþdemo.þ FPulse=Clk:1.0s ~END_CIRCUIT~ È Fn#0,4477 ' Init - system initialization code ' SetBAUD 1,6 ' RS-232 port 34.8K 8,1,N (HMI interface) if strcmp(EEPLayout, Load_EEP$(EEPLayout_S)) <> 0 ' This verison of the program does not match the format of the data stored in EEPROM. ' ' OK, let's start by clearing out the entire EEPROM memory (zero fill) ' for i = 1 to 5500 Save_EEP32 0,i next ' We need to "build" the correct data pattern into EEPROM ' Save_EEP$ EEPLayout, EEPLayout_S Save_EEP$ ProgramName, ProgramName_S Save_EEP$ ProgramVersion, ProgramVersion_S ' We Need to build the strapping tables for each tank in EEP32 ' j = TankID_Base_S ' Index into EEP$ for tank ID message i = StrappingTbl_I ' Index into EEP32 to start building strapping tables for n = 1 to MaxTankNo ' for each strappping table Save_EEP$ "Tank "+str$(n)+" Default Values", j Save_EEP32 117000*95/100,I+0 ' 95% of tank volume. "High Alarm" level Save_EEP32 117000*97/100,I+1 ' 97% of tank volume. "High High Alarm" level Save_EEP32 117000/100,I+2 ' 1% of tank volume. Save_EEP32 2174,I+3 ' mm/uA slope Save_EEP32 -870,I+4 ' mm/uA offset Save_EEP32 10000,I+5 ' mm/uA scale Save_EEP32 0,I+6 ' Reserved Save_EEP32 0,I+7 ' Reserved Save_EEP32 0,I+8 ' start of strapping table 4.0 mA Save_EEP32 1044,I+9 Save_EEP32 2949,I+10 Save_EEP32 5385,I+11 Save_EEP32 8201,I+12 Save_EEP32 11374,I+13 Save_EEP32 14827,I+14 Save_EEP32 18481,I+15 Save_EEP32 22371,I+16 Save_EEP32 26434,I+17 Save_EEP32 30604,I+18 Save_EEP32 34933,I+19 Save_EEP32 39363,I+20 Save_EEP32 43871,I+21 Save_EEP32 48395,I+22 Save_EEP32 53000,I+23 Save_EEP32 57625,I+24 Save_EEP32 62208,I+25 Save_EEP32 66817,I+26 Save_EEP32 71388,I+27 Save_EEP32 75863,I+28 Save_EEP32 80302,I+29 Save_EEP32 84645,I+30 Save_EEP32 88868,I+31 Save_EEP32 92912,I+32 Save_EEP32 96824,I+33 Save_EEP32 100537,I+34 Save_EEP32 103987,I+35 Save_EEP32 107196,I+36 Save_EEP32 110079,I+37 Save_EEP32 112544,I+38 Save_EEP32 114522,I+39 ' last entry of strapping table 19.5 mA j = j + 1 ' next ID string is stored here i = i + StrappingTblSize ' next strapping table in EEP32 next else ' Same EEPROM layout, but other stuff may have changed ' if strcmp(ProgramVersion, Load_EEP$(ProgramVersion_S)) <> 0 ' Different program version ' Save_EEP$ ProgramVersion, ProgramVersion_S endif endif ' At this point the data stored in EEPROM is correct ' Now we must build the DataArray used to manage tank information for each tank ' ' For each tank the following information is maintained: ' ' DM32[z+0] AverageADC filtered ADC value upsamped to 13 bits (0..8192) ' DM32[z+1] mA calculated mA value from AverageADC, 4..20 mA ' DM32[z+2] mm calcultated fluid level in mm ' DM32[z+3] Volume Calculated tank volume in liters ' DM32[z+4] Ullage Calculated head space. ' DM32[z+5] TankStatus Current status of tank (values TBD) ' DM32[z+6] Reserved Not Used. ' DM32[z+7] TankCapacity Safe Fill volume for this tank. ' DM32[z+8] Ptr2STbl This is the address in EEP32 that holds the first entry of the ' strapping table data for this tank. ' DM32[z+9] .. DM32[z+19] buffer space for tank levesl used to compute the dvdt ' change of tank volume over time values. ' ' Please note that #Define macros are used to make the code a little easier to read and a lot ' easier to maintain. ' y = StrappingTbl_I ' Starting index in EEP32 for the first strapping table z = DataArrayStart ' Starting index in DM32[] for the first data element for the first tank SysCapacity = 0 ' Starting value to compute total safe system volume for i = 1 to MaxTankNo ' for each tank AverageADC = ADC(i) * 2 ' Initialize low pass filter for ADC value ' attempt to increase resolution to 0..8192 Ptr2Stbl = y ' save start of EEP data for later use TankCapacity = Level95Pct ' get safe fill volume for this tank SysCapacity = SysCapacity + TankCapacity ' add in safe volume for this tank to the system total Call WriteTankData ' build file for FTP download y = y + StrappingTblSize ' start of next strapping table in EEP32 z = z + DataArraySize ' start of next DataArray in DM32[] next ' The fuel island volumes are saved in EEPROM. Fetch them and update the DM32[] versions ' FP1Volume = Load_EEP32(FP1EEPSave) FP2Volume = Load_EEP32(FP2EEPSave) FP3Volume = Load_EEP32(FP3EEPSave) FP4Volume = Load_EEP32(FP4EEPSave) È Fn#1,7323 ' ReadADCs - function to read the current value of ADCs [1..6] ' The raw ADC value is converted to the range of 0.00 to 20.00 ma for all ADCs and these ' values are saved in DM32. The converted values are saved in units of 0.01 ma ' ' On exit: ' the mA and Volume in liters values for each tank will be updated ' in DM32DM32[RawADCValues ...] updated ' global variables i, x and z have been affected ' z = DataArrayStart ' Starting address in DM32[] for data sturcture for ' the tank connected to ADC(i). ' Please note that "z" is used by #define macros ' to make the code a little more robust and a lot easier to read. for i = 1 to MaxTankNo ' for these ADC(i) inputs ' low pass iir filter - this is a simple single pole Infinite impluse response filter. ' This filter is the a digital verion of a low pass RC filter. ' This filter is intended to filter out random noise in the ADC conversion. This way we do not ' "see" the jitter in the mA, mm, Volume and Ullage calcuateions that are just random noise. ' ' This filter has a time constant of about 20 seconds. As the tank levels cannot change very ' quickly, this long time constant is acceptable. ' ' A second trick is being played with the implementatino of the iir filter. ' I am attempting to double the resolution of the PLC's ADC from 12 to 13 bits by oversampling. ' The ouptut of this filter will be in the range of 0..8191 (13 bit ADC) rather than ' 0..4095 (actual raw 12 bit ADC). ' ' The decay rate of the filter is determined by the 15 and 16 coeffiencts. The "new" value ' of this filter is based on 31/32 of the "old" value and 1/32 of the new ADC reading. ' AverageADC = (AverageADC * 31 + ADC(i)*2)/32 ' The conversion from ADC introplated values to mA is carried out by the following values: ' ' 8191 when the inuput current is 20 mA ' 0 when the input current is 0 mA. ' ' This relationship between ma and ADC values is as follows: ' ' y = x * 2.4417043 + 0 ' ' where: ' y is mA in 0.001 mA units ' x is raw ADC values ' 2.4417043 is the slope (m) of the line ' 0 is the "y intercept" (b). As b is 0, this is the last time you'll see this term ' ' But, the FMD PLC does not support floating point math! We have to used scaled integers ' ' Y = (x * 24417) / 10000 ' ' where: ' Y is mA x 1000 (0.001 mA resolution) ' x is raw ADC values ' mA = (AverageADC * 24417) / 10000 ' save calculated mA value from level transducer if (mA >= 4000) ' The transducer is probably working ' ' First calculate the mm of liquid in the tank based on the mA value ' y = Ptr2Stbl ' Y holds address in EEPROM of start of strapping table ' data structure for this tank ' y is used in the following macro definitions: ' mmConvSlope - slope for the mm/ma equation ' mmConvOffset - offset for the mm/ma equation ' mmConvScale - scale factor the the mm/ma equation ' StblStartAddr - address in EEPROM of first entry of ' the strapping tabel for this tank mm = mA * mmConvSlope / mmConvScale + mmConvOffset ' There are 32 strapping table entries in the table in EEPROM. ' The first entry is the tank volume in liters at 4.000 mA ' The last (32nd entry) is the tank volume in liters at 19.500 mA. ' ' Remember that the mA value is actually mA x 1000 ' x = (mA - 4000)/500 ' x is in the range of 0..31 (mA values 4.000, 4.500, ... 19.500) if (x < 31) ' We are within the strapping table range so it is possible to interpolate ' a volume that lies between the entries. A linear interpolation will be ' performed to calculate the volume bewteen table entries. ' V = (Load_EEP32(STblStartAddr+x+1)-Load_EEP32(STblStartAddr+x))*(mA MOD 500)/500 else ' the X value is for the last entry in the strappping table, 19.5 mA. ' the table does not contain an entry for 20.0 mA so we are going to cheat and ' use the difference between the 19.0 and 19.5 mA volumes to compute a slope for ' interpolation. ' ' Yes, this is wrong and morally reprehensible, but this tank shouldn't be this ' full and the volume error is small. ' V = (Load_EEP32(STblStartAddr+x)-Load_EEP32(STblStartAddr+x-1))*(mA MOD 500)/500 endif ' Now add in the volume from the strapping table ' Volume = V + Load_EEP32(STblStartAddr + x) ' Now calculate the Ullage (remaining head space) for this tank ' Ullage = Level95Pct - Volume ' 95% of capacity Ullage97 = Level97Pct - Volume ' 97% of capacity ' Now manage the RELAYS that are used to track tank volume status ' Two RELAYS are associted with each tank: ' TnLevel95 and TnLevel97 (n = tank # 1..6) ' ' There is a hysteresis of 1% of the maximum tank capacity so that if a RELAY gets set, the level ' must drop by 1% before this RELAY will be cleared. ' if i = 1 ' Tank #1 ClrIO T1SensorFail ' tank sensor is working if (Ullage < 0) SetIO T1Level95 elif Ullage > Level01Pct ClrIO T1Level95 endif if (Ullage97 < 0) SetIO T1Level97 elif Ullage97 > Level01Pct ClrIO T1Level97 endif elif i = 2 ' Tank #2 ClrIO T2SensorFail ' tank sensor is working if (Ullage < 0) SetIO T2Level95 elif Ullage > Level01Pct ClrIO T2Level95 endif if (Ullage97 < 0) SetIO T2Level97 elif Ullage97 > Level01Pct ClrIO T2Level97 endif elif i = 3 ' Tank #3 ClrIO T3SensorFail ' tank sensor is working if (Ullage < 0) SetIO T3Level95 elif Ullage > Level01Pct ClrIO T3Level95 endif if (Ullage97 < 0) SetIO T3Level97 elif Ullage97 > Level01Pct ClrIO T3Level97 endif elif i = 4 ' Tank #4 ClrIO T4SensorFail ' tank sensor is working if (Ullage < 0) SetIO T4Level95 elif Ullage > Level01Pct ClrIO T4Level95 endif if (Ullage97 < 0) SetIO T4Level97 elif Ullage97 > Level01Pct ClrIO T4Level97 endif elif i = 5 ' Tank #5 ClrIO T5SensorFail ' tank sensor is working if (Ullage < 0) SetIO T5Level95 elif Ullage > Level01Pct ClrIO T5Level95 endif if (Ullage97 < 0) SetIO T5Level97 elif Ullage97 > Level01Pct ClrIO T5Level97 endif elif i = 6 ' Tank #6 ClrIO T6SensorFail ' tank sensor is working if (Ullage < 0) SetIO T6Level95 elif Ullage > Level01Pct ClrIO T6Level95 endif if (Ullage97 < 0) SetIO T6Level97 elif Ullage97 > Level01Pct ClrIO T6Level97 endif endif else ' The transducer may not be working (broken cable?). The expected minimum transducer ' output should be 4.000 mA. This could also be a problem with calibration ' Volume = 0 ' unknown mm = 0 ' unknown Ullage = 0 ' unknown if (mA < 3960) ' Sensor output is more than 1% low. This is a bigger problem than calibration. ' This is most likely a broken cable or a dead transducer. ' Set a RELAY to track the failure ' if i = 1 SetIO T1SensorFail elif i = 2 SetIO T2SensorFail elif i = 3 SetIO T3SensorFail elif i = 4 SetIO T4SensorFail elif i = 5 SetIO T5SensorFail elif i = 6 SetIO T6SensorFail endif endif endif z = z + DataArraySize ' next DataArray start index next ' next tank number È Fn#2,2022 ' CalcDvDt - calculate rate of system/tank volume ' ' What we are trying to do is dectect when the system/tank is being filled. ' ' Based on input from Bob, this is the set of assumptions that I am working with: ' ' 1. The tanker transfer pump operates between 400 and 1000 liters per minute. ' 2. The tanker delivery can be between 4000 and 37500 liters. ' ' At the minimum pump rate of 400 l/m and the maximum volume to be delivered of 37500 l, then the ' delivery could be completed in 93 minutes. ' ' At the maximum pump rate of 1000 l/m and a minimum delivery of 4000 l, then the delivery could be ' completed in 4 minutes. ' ' This code maintains a circular queue that holds the last 10 samples of the total system volume in liters. Each ' time this custom function is called, the current system volume is appended to the queue. ' ' Once the queue is filled to capacity, then the oldest value is poped (removed) from the queue and compared with the ' current system value and the rate of change of system volume, SysVolDvDt is calculated. The current volume is then ' appended to the queue. ' if TestIO(BufferFull) ' Circular buffer is full so a rate of change value (dvdt) can be calculated ' ' dvdt is in the units of liters per minute ' SysVolDvDt = (SystemVolume - DM32[SysBufferBase + BufferIndex]) / CircBufferSize ' Update tracking RELAY, FillEvent, to track the status of fill events ' if SysVolDvDt > 400 SetIO FillEvent ' Fill event elif SysVolDvDt <= 0 ClrIO FillEvent ' Not a fill event endif endif ' Add current SystemVolume to circular queue. ' If the queue is full, then this entry will overwrite the oldest value in the buffer ' DM32[SysBufferBase + BufferIndex] = SystemVolume ' Update the BufferIndex value. This value must be kept between the values 0 and CircBufferSize-1. ' The MOD operator does the work... ' BufferIndex = (BufferIndex + 1) MOD CircBufferSize if (BufferIndex = 0) SetIO BufferFull ' Buffer is now full and will never be empty, again... endif È Fn#3,1920 ' UpdateStatus - Now that all of the tank measurements have been made, ' this function will update tracking RELAYS based on ' the current state of the system. ' A = 0 ' temp variable for SystemVolume to prevent the HMI from "seeing" an ' intermediate value during the running of the for/next loop B = 0 ' temp variable for SystemUllage (same issue as above) z = DataArrayStart ' Starting address in DM32[] for data sturcture for for i = 1 to MaxTankNo ' for these tanks // The TankStatus is is a bit map. The the bit order has been set to match the HMI's LSB // bit patter that treats the least significant bit has having the most importance. // So if this case if T1Level95 and T1SensorFail are set to 1 then the HMI will decide // that the T1SensorFail most important bit to display if i = 1 TankStatus = TestIO(T1Level95)*8 | TestIO(T1Level97)*4 | TestIO(T1SensorFail)*2 elif i = 2 TankStatus = TestIO(T2Level95)*8 | TestIO(T2Level97)*4 | TestIO(T2SensorFail)*2 elif i = 3 TankStatus = TestIO(T3Level95)*8 | TestIO(T3Level97)*4 | TestIO(T3SensorFail)*2 elif i = 4 TankStatus = TestIO(T4Level95)*8 | TestIO(T4Level97)*4 | TestIO(T4SensorFail)*2 elif i = 5 TankStatus = TestIO(T5Level95)*8 | TestIO(T5Level97)*4 | TestIO(T5SensorFail)*2 elif i = 6 TankStatus = TestIO(T6Level95)*8 | TestIO(T6Level97)*4 | TestIO(T6SensorFail)*2 endif A = A + Volume ' Total of all of the individual tank volumes B = B + Ullage ' Total of all of the indivudual ullages z = z + DataArraySize ' next Data Array start index next SystemVolume = A ' Total of all of the individual tank volumes SystemUllage = B ' Total of all of the indivudual ullages ' Now figure out the status for the complete tank system ' SystemStatus = TestIO(ValidFillEvent)*32 | TestIO(InvalidFillEvent)*16 | TestIO(T95Alarm)*8 | TestIO(T97Alarm)*4 | TestIO(SensorFailure)*2 | TestIO(BundAlarm) È Fn#4,738 ' WriteTankData - create file for tank specified by the integer I ' ' To get line termination for DOS text files, the following was appended: ' ' ;"\0d\0a"; ' ' Otherwise the line termination is just ... ' n = I - 1 ' array offset (0..5) PRINT #8 "" ' Open file PRINT #8 str$(I);"\0d\0a"; ' Tank number (1..6) PRINT #8 Load_EEP$(TankID_Base_S + n);"\0d\0a"; ' Tank ID string ' J is the address in EEP32 for strapping table data for tank I ' j = StrappingTbl_I + StrappingTblSize * n k = StrappingTblSize while (k) PRINT #8 Load_EEP32(j);"\0d\0a"; ' numeric value j = j + 1 k = k - 1 endwhile PRINT #8 "";"\0d\0a"; ' End of file marker PRINT #8 "" ' Close file È Fn#5,2553 ' NewDataTable - Custom function that checks for existance of a newly received ' file (via FTP) with the name of "Z000.TXT". This file contains a ' "new" version of a tank strapping table. ' ' If a "new" file has been received, it will be parsed the PLC data will be updated ' and the "new" file will be deleted. ' ' Variable affected by this function: ' i,j,k,n,o and A$ PRINT #8 "" ' Attempt to open file if status(2) <> 1 ' File did not open, probably because a file is already open ' this is a programming error ' PRINT #8 "" ' Close other file that is open return ' and bail out endif if status(19) = 0 ' File exists but contains no data. This is a useless file. ' ' The reality is the the PLC's "Extended File System" is extremly primative ' and is not accurately documented. You actually can't tell if a file exists ' but you can tell if a file contains data. Status(19) returns the file size ' PRINT #8 "" ' Close file return ' and bail out endif n = 1 ' line count within the open file while(1) ' Read each line in the file ' ' The extended file system has an interesting behavior that is not documented, it can ' tolerate text files that are terminated with either a single CR or a CR/LF pair. In either ' case the line terminators are do not show up in the returned string. ' A$ = INPUT$(8) if (status(2) = 255) ' unexpected end of file PRINT #8 "" ' Close file return ' and bail out endif if (n = 1) ' Tank number field i = val(A$) ' I is a tank number (1..4) o = i - 1 ' I is a tank number as an offset (0..3) if (o < 0) OR (o > 3) ' Tank offset is invalid ' PRINT #8 "" ' Close file return ' and bail out else ' Tank offset is valid, calculate offset into EEP32 ' for start of the strapping table data ' j = StrappingTbl_I + o * StrappingTblSize endif elif (n = 2) ' Tank ID string field Save_EEP$ A$, TankID_Base_S + o elif (n < 43) ' Save the strapping table data in EEP32 ' and bump up the index, j, to next EEP32 address ' Save_EEP32 val(A$), j j = j + 1 elif (strcmp(A$,"") = 0) ' we have reached the logical end of file and are nearly ' finished ' exit ' exit the while loop and finish the job endif n = n + 1 ' next line number of source file endwhile ' delete the file to indicate that it was valid and has been used. ' PRINT #8 "" PRINT #8 "" Call WriteTankData ' now update the file associated with this tank È Fn#11,142 ' CountFP1 - acculate pulses for Fuel Island pump 1 ' FP1Volume = FP1Volume + 10 ' each pulse is 10 liters Save_EEP32 FP1Volume, FP1EEPSave È Fn#12,142 ' CountFP2 - acculate pulses for Fuel Island pump 2 ' FP2Volume = FP2Volume + 10 ' each pulse is 10 liters Save_EEP32 FP2Volume, FP2EEPSave ~END_CUSTFN~ 0,Init 1,ReadADCs 2,CalcDvDt 3,UpdateStatus 4,WriteTankData 5,NewDataTable 11,CountFP1 12,CountFP2 ~END_CUSTFNLABEL~ 0,0, 1,0, 2,0, 3,0, 4,0, ~END_QUICKTAGS~ 0,EEPLayout,"EEP_V1.02" 1,EEPLayout_S,1 2,ProgramName,"DSGaging" 3,ProgramName_S,2 4,ProgramVersion,"V1.07" 5,ProgramVersion_S,3 7,MaxTankNo,4 9,TankID_Base_S,4 10,TankID_2S,5 11,TankID_3S,6 12,TankID_4S,7 13,TankID_5S,8 14,TankID_6S,7 16,SystemStatus,DM32[1] 17,SystemVolume,DM32[2] 18,SystemUllage,DM32[3] 19,FP1Volume,DM32[4] 20,FP2Volume,DM32[5] 21,FP3Volume,DM32[6] 22,FP4Volume,DM32[7] 23,SysCapacity,DM32[8] 24,SysVolDvDt,DM32[9] 28,FP1EEPSave,2 29,FP2EEPSave,3 30,FP3EEPSave,4 31,FP4EEPSave,5 33,StrappingTbl_I,10 34,StrappingTblSize,40 36,Level95Pct,Load_EEP32(y+0) 37,Level97Pct,Load_EEP32(y+1) 38,Level01Pct,Load_EEP32(y+2) 39,mmConvSlope,Load_EEP32(y+3) 40,mmConvOffset,Load_EEP32(y+4) 41,mmConvScale,Load_EEP32(y+5) 42,STblStartAddr,(y+8) 44,DataArrayStart,11 45,DataArraySize,20 46,AverageADC,DM32[z+0] 47,mA,DM32[z+1] 48,mm,DM32[z+2] 49,Volume,DM32[z+3] 50,Ullage,DM32[z+4] 51,TankStatus,DM32[z+5] 52,Ullage97,DM32[z+6] 53,TankCapacity,DM32[z+7] 54,Ptr2STbl,DM32[z+8] 56,CircBufferSize,10 57,BufferIndex,DM32[201] 58,SysBufferBase,202 ~END_DEFINES~ ~END_BREAKPOINTS~ 192.168.1.16:9080 ~END_LASTIPADDR~