#!/usr/bin/env python from datetime import datetime import time import mysql.connector as mysqlcon from pycomm.ab_comm.clx import Driver as ClxDriver from tag.tag_mysql import Tag from tag.tag_mysql import AnalogAlarm from tag.tag_mysql import bitAlarm from readConfig import readConfig import traceback import pickle import os with open(os.path.realpath('.') + '/mysql_cfg.pickle', 'rb') as cfgFile: mysql_cfg = pickle.load(cfgFile) con = mysqlcon.connect(**mysql_cfg) try: configProperties = readConfig() except: traceback.print_exc() def readTag(addr, tag): time.sleep(0.01) c = ClxDriver() if c.open(addr): try: v = c.read_tag(tag) # print(v) return v except Exception: print("ERROR RETRIEVING TAG: {}".format(tag)) c.close() print traceback.print_exc() pass c.close() def readArray(addr, arr, length): # logging.basicConfig(filename="clx.log", format="%(levelname)-10s %(asctime)s %(message)s", level=logging.DEBUG) c = ClxDriver() if c.open(addr): try: v = c.read_array(arr, length) # print(v) return map(lambda x: x[1], v) except Exception: print("ERROR RETRIEVING ARRAY: {}".format(arr)) err = c.get_status() c.close() print err pass c.close() def checkDateInDB(da): y = int(da[0:4]) m = int(da[4:6]) d = int(da[6:8]) dquery = "SELECT id FROM card_history_dates WHERE year = {0} AND month = {1} AND day = {2};".format(y, m, d) # dquery = "SELECT id FROM WellData.card_history_dates WHERE year = 2016 AND month = 1 AND day = 5;" con.connect() cur = con.cursor() cur.execute(dquery) dates = cur.fetchall() if len(dates) > 0: print("Date {0} already in db".format(da)) else: ins_query = "INSERT INTO card_history_dates (year, month, day, first_id) VALUES ({0}, {1}, {2}, (SELECT MAX(id) FROM card_history));".format(y, m, d) print(ins_query) con.connect() cur = con.cursor() cur.execute(ins_query) con.commit() class Status(Tag): def sendToDB(self): query = "INSERT INTO run_status (dtime, status) VALUES ({}, '{}')".format(time.time(), self.value) print query con.connect() cur = con.cursor() cur.execute(query) con.commit() self.last_send_time = time.time() # ---------- MAP FUNCTIONS ---------- # maps = { 'modeMap': { 0: "Error", 1: "Auto", 2: "POC", 3: "Timer", 4: "Manual", 5: "DH PID" }, 'card_type_map': { 0: "Normal", 1: "Shutdown", 2: "Alarm", 3: "Startup", 4: "Low Fillage" }, 'statusMap': { 0: 'Stopped', 1: 'Running', 2: 'Pumped Off', 3: 'Faulted', 4: 'Starting', 5: 'Recovering', 100: 'Read Error', 1000: 'PLC Error', 9999: 'No Response' }, 'conditionMap': { 20: "Low", 21: "High", 24: "LoLo", 25: "HiHi", 32: "Input Failure", 34: "Configuration Error", 16: "Failure to Stop", 17: "Failure to Start", 18: "Drive Fault" } } # ---------- TAGS ---------- # stroke_tags = {} # Tags stored for every single stroke history_tags = {} # Tags stored on value change or age gaugeoff_tags = {} # Tags stored at gauge off welltest_tags = {} # Tags stored at well test submit bit_tags = {} safety_tags = {} custom_tags = {} status = Status('run_status', 'Pump.Run_Status', 0, 'STRING', 0, 3600, mapFn=maps['statusMap'], ip_address=configProperties['PLC_IP_ADDRESS']) def setupTags(): con.connect() cur = con.cursor() query = "SELECT t.name as name, c.tag_class as class, t.tag as tag, t.data_type as data_type, t.change_threshold as change_threshold, t.guarantee_sec as guarantee_sec, t.id as id, t.map_function as map_function FROM tags t JOIN tag_classes c ON c.id = t.class;" cur.execute(query) tags = cur.fetchall() # (u'downhole_gross_stroke', u'history', u'Card_Past[1].Downhole_GrossStroke', u'REAL', 2.0, 3600, 6, None) # 0: name, 1: class, 2: tag, 3: data_type, 4: change_threshold, 5: guarantee_sec, 6: db id, 7: map_function for x in tags: print(x) if str(x[1]) == 'stroke': if x[7]: stroke_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5], mapFn=maps[str(x[7])]) else: stroke_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5]) elif str(x[1]) == 'history': if x[7]: history_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5], mapFn=maps[str(x[7])]) else: history_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5]) elif str(x[1]) == 'gaugeoff': if x[7]: gaugeoff_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5], mapFn=maps[str(x[7])]) else: gaugeoff_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5]) elif str(x[1]) == 'welltest': if x[7]: welltest_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5], mapFn=maps[str(x[7])]) else: welltest_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5]) elif str(x[1]) == 'custom': if x[7]: custom_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5], mapFn=maps[str(x[7])]) else: custom_tags[x[0]] = Tag(str(x[0]), str(x[2]), x[6], str(x[3]), x[4], x[5]) con.connect() cur = con.cursor() query = "SELECT c.alarm_class as class, a.name as name, a.tag as tag, a.cond as cond, a.id as id FROM alarms a JOIN alarm_classes c ON a.class = c.id;" cur.execute(query) alarms = cur.fetchall() for x in alarms: # 0: class, 1: name, 2: tag, 3: condition if str(x[0]) == 'analog': safety_tags[x[1]] = AnalogAlarm(str(x[1]), str(x[2]), int(x[4]), device_type="CLX", ip_address=configProperties['PLC_IP_ADDRESS']) elif str(x[0]) == 'bit': bit_tags[x[1]] = bitAlarm(str(x[1]), str(x[2]), str(x[3]), int(x[4]), device_type="CLX", ip_address=configProperties['PLC_IP_ADDRESS']) print('===== STROKE TAGS =====') for t in stroke_tags: print(t) print('===== HISTORY TAGS =====') for t in history_tags: print(t) print('===== WELLTEST TAGS =====') for t in welltest_tags: print(t) print('===== GAUGEOFF TAGS =====') for t in gaugeoff_tags: print(t) print('===== BIT SAFETIES =====') for t in bit_tags: print(t) print('===== ANALOG SAFETIES =====') for t in safety_tags: print(t) print('===== CUSTOM TAGS =====') for t in custom_tags: print(t) setupTags() def readPoints(): global configProperties num_points = readTag(configProperties['PLC_IP_ADDRESS'], "Card_Past[1].Num_Points")[0] surf_pos = readArray(configProperties['PLC_IP_ADDRESS'], "Card_Past[1].Surface_Position", num_points + 1)[1:] surf_pos.append(surf_pos[0]) surf_lod = readArray(configProperties['PLC_IP_ADDRESS'], "Card_Past[1].Surface_Load", num_points + 1)[1:] surf_lod.append(surf_lod[0]) down_pos = readArray(configProperties['PLC_IP_ADDRESS'], "Card_Past[1].Downhole_Position", num_points + 1)[1:] down_pos.append(down_pos[0]) down_lod = readArray(configProperties['PLC_IP_ADDRESS'], "Card_Past[1].Downhole_Load", num_points + 1)[1:] down_lod.append(down_lod[0]) return([surf_pos, surf_lod, down_pos, down_lod]) def evalTapers(): global configProperties ts = time.time() numTapers = int(readTag(configProperties['PLC_IP_ADDRESS'], 'Card_Current.Params.Num_Tapers')[0]) for t in range(1, numTapers + 1): taper_length = readTag(configProperties['PLC_IP_ADDRESS'], 'Taper.Taper[{}].Setup.Length'.format(t))[0] taper_diameter = readTag(configProperties['PLC_IP_ADDRESS'], 'Taper.Taper[{}].Setup.Diameter'.format(t))[0] taper_material = readTag(configProperties['PLC_IP_ADDRESS'], 'Taper.Taper[{}].Setup.Material'.format(t))[0] if (taper_material == 1): taper_material = "Steel" elif (taper_material == 2): taper_material = "Fiberglass" tStr = "{{'taper':{}, 'length': {}, 'diameter': {}, 'material':'{}'}}".format(t, taper_length, taper_diameter, taper_material) tQuery = 'INSERT INTO well_config (tstamp, type, val) VALUES ({}, "taper", "{}")'.format(ts, tStr) print tQuery con.connect() cur = con.cursor() cur.execute(tQuery) con.commit() pump_diameter = readTag(configProperties['PLC_IP_ADDRESS'], 'UnitConfig.Pump_Diameter')[0] cfgQuery = "INSERT INTO well_config (tstamp, type, val) VALUES ({}, 'pump_diameter', '{}')".format(ts, pump_diameter) con.connect() cur = con.cursor() cur.execute(cfgQuery) con.commit() print "TAPER DATA READ!" return True def main(): global configProperties read_tapers = False already_gauged_off = False already_entered_well_test = False last_date = "" last_stroke = 0 last_status = "" statusChanged = False while True: try: current_status = status.read("test") statusChanged = not (current_status == last_status) if statusChanged: last_status = current_status ############# # CARD DATA # ############# stroke_tags['card_id'].read('test') if not (last_stroke == stroke_tags['card_id'].value): sData = {} last_stroke = stroke_tags['card_id'].value stroke_time = time.time() dt = datetime.fromtimestamp(stroke_time) sData['localtime'] = dt sData['stroke_time'] = dt sData['utctime'] = datetime.utcfromtimestamp(stroke_time) for t in stroke_tags: if not t == "card_id": stroke_tags[t].read(True) [sData['Surface_Position'], sData['Surface_Load'], sData['Downhole_Position'], sData['Downhole_Load']] = readPoints() # st = datetime.strftime(dt, "%Y%m%d_%H%M%S") date = datetime.strftime(dt, "%Y%m%d") if not date == last_date: checkDateInDB(date) last_date = date sData["card_type"] = stroke_tags['card_type'].value sData["card_id"] = stroke_tags['card_id'].value sData['sp_string'] = ', '.join(map(str, sData['Surface_Position'])) sData['sl_string'] = ', '.join(map(str, sData['Surface_Load'])) sData['dp_string'] = ', '.join(map(str, sData['Downhole_Position'])) sData['dl_string'] = ', '.join(map(str, sData['Downhole_Load'])) insert_query = "INSERT INTO card_history (Card_ID, Card_Type, Stroke_Time, Surface_Position, Surface_Load, Downhole_Position, Downhole_Load) VALUES (:card_id, :card_type, :stroke_time, :sp_string, :sl_string, :dp_string, :dl_string)" con.connect() cur = con.cursor() cur.execute(insert_query, sData) con.commit() print "CARD NUMBER " + str(sData["card_id"]) + " READ!" ################### # HISTORICAL DATA # ################### for hist in history_tags: h = history_tags[hist] h.read("test") for cust in custom_tags: t = custom_tags[cust] t.read("test") ############## # TAPER DATA # ############## update_taper = readTag(configProperties['PLC_IP_ADDRESS'], "Write_Tapers")[0] > 0 if (update_taper == 0): if read_tapers: read_tapers = False print "Update Tapers = False" if (update_taper and (not read_tapers)): print "reading taper file" read_tapers = evalTapers() ################## # GAUGE OFF DATA # ################## gauge_off = readTag(configProperties['PLC_IP_ADDRESS'], "Gauge_Off_Command")[0] if (gauge_off == 0): if already_gauged_off: already_gauged_off = False print "Already gauged off... Setting gauge_off to False" if (gauge_off and (not already_gauged_off)): print "Gauging off..." for goff in gaugeoff_tags: g = gaugeoff_tags[goff] g.read(True) gauge_date = datetime(year=gaugeoff_tags['year'].value, month=gaugeoff_tags['month'].value, day=gaugeoff_tags['day'].value, hour=gaugeoff_tags['hour'].value, minute=gaugeoff_tags['min'].value, second=gaugeoff_tags['sec'].value) con.connect() cur = con.cursor() con.execute("""INSERT INTO Hist_Day (gauge_date, percent_run, kWh, electricity_cost, peak_load, min_load, average_SPM, production_calculated, full_card_production, polished_rod_HP, lifting_cost, fluid_above_pump, pump_intake_pressure, kWh_regen, inflow_rate) VALUES ('%s', '%f', '%f', '%f', '%f', '%f', '%f', '%f', '%f', '%f', '%f', '%f', '%f', '%f', '%f');""" % (gauge_date, gaugeoff_tags['percent_run'].value, gaugeoff_tags['kwh'].value, gaugeoff_tags['electricity_cost'].value, gaugeoff_tags['max_load'].value, gaugeoff_tags['min_load'].value, gaugeoff_tags['average_spm'].value, gaugeoff_tags['production_calculated'].value, gaugeoff_tags['full_card_production'].value, gaugeoff_tags['polished_rod_hp'].value, gaugeoff_tags['lifting_cost'].value, gaugeoff_tags['fluid_level'].value, gaugeoff_tags['pump_intake_pressure'].value, gaugeoff_tags['kwh_regen'].value, gaugeoff_tags['inflow_rate'].value)) con.commit() already_gauged_off = True print "Gauged off!" ################## # WELL TEST DATA # ################## well_test_entered = readTag(configProperties['PLC_IP_ADDRESS'], "Well_Test.Test_Submit")[0] > 0 if (well_test_entered == 0): if already_entered_well_test: already_entered_well_test = False print "Already entered well Test... Setting well_test_entered to False" if (well_test_entered and (not already_entered_well_test)): for wtest in welltest_tags: w = welltest_tags[wtest] w.read(True) print "Well Test Entered" print('{}/{}/{} {}:{}:{}'.format(welltest_tags['year'].value, welltest_tags['month'].value, welltest_tags['day'].value, welltest_tags['hour'].value, welltest_tags['min'].value, welltest_tags['sec'].value)) test_date = datetime(year=welltest_tags['year'].value, month=welltest_tags['month'].value, day=welltest_tags['day'].value, hour=welltest_tags['hour'].value, minute=welltest_tags['min'].value, second=welltest_tags['sec'].value) con.connect() cur = con.cursor() test_query = "INSERT INTO Well_Test (test_date, test_volume_oil, test_volume_water, test_volume_gas, k_factor, projected_volume_oil, projected_volume_water, api_gravity_oil, sg_water, test_hours) VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}');".format(test_date, welltest_tags['v_oil'].value, welltest_tags['v_water'].value, welltest_tags['v_gas'].value, welltest_tags['k_factor'].value, welltest_tags['p_v_oil'].value, welltest_tags['p_v_water'].value, welltest_tags['api_oil'].value, welltest_tags['sg_water'].value, welltest_tags['test_duration'].value) # print test_query con.execute(test_query) con.commit() already_entered_well_test = True print "Well Test Stored!" ################### # ALARMS & EVENTS # ################### for t in safety_tags: safety_tags[t].checkStatus(stroke_tags['card_id'].value) for b in bit_tags: bit_tags[b].checkStatus(stroke_tags['card_id'].value) time.sleep(.20) except Exception, e: print("Error during loop: {}".format(e)) traceback.print_exc() if __name__ == '__main__': main()