Files
sf_lactor/lactor.py
2019-09-10 22:00:41 +02:00

378 lines
14 KiB
Python

#!/usr/bin/env python3
import sys, os
from pathlib import Path
import can
from DB import *
from time import sleep, time, strftime
from datetime import *
from configparser import *
from Taurus import *
from socket import *
from select import select
bus = None
infotype = {0 : 'S-Buffer deleted',
1 : 'Reserved 1',
2 : 'command',
3 : 'milking time',
4 : 'animal nr',
5 : 'milkammount',
6 : 'state'
}
grfid = '0'
class CanListener (can.Listener):
def on_message_received (self, msg):
self.log ("Received Data\n==========================", 3)
self.CanDecode (msg)
def set_db (self, dbcon):
self.db_con = dbcon
self.dataset = [None] * 128
self.waitack = [0] * 128
self.waitackst = [0] * 128
self.loglevel = 9
self.log("Init Vars", 0)
#[arbitration_id', 'data', 'dlc', 'id_type', 'is_error_frame', 'is_extended_id', 'is_remote_frame', 'timestamp']
#<class 'can.message.Message'>
#Timestamp: 1499861978.530813 ID: 0581 000 DLC: 8 20 20 20 20 30 30 30 30
def log (self, msg, _level = 3):
"""
Logfile
Loglevel 9 = print to console
"""
dt = datetime.now ()
if self.loglevel == 9:
print ("%4d-%02d-%02d %02d:%02d:%02d.%06d: " %(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond) + msg)
if _level <= self.loglevel:
file = open ("/var/log/lactor/" + os.getcwd().split('/')[-1] + '.err', "a")
file.write ("%4d-%02d-%02d %02d:%02d:%02d.%06d: " %(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond) + msg + "\n")
file.close ()
def CanDecode (self, msg):
"""
Decoding Can Telegram
"""
if msg.is_extended_id:
self.log ("Msg Mit Erweiterter ID erhalten", 0)
return
if msg.is_error_frame:
self.log ("Msg Error Frame erhalten", 0)
return
if msg.is_remote_frame:
self.log ("Msg Remote Frame erhalten", 0)
return
_id = msg.arbitration_id
self.log ("Msg: %s" % msg, 9)
#Pos 11 Direction
if not is_set (_id, 10):
self.log ("Daten fur xLactor Steuerung erhalten", 0)
return
#Pos 8-10 Infotype
_info_type = (_id & 0b01110000000) >> 7
self.log ("Infotyp: %s %s" % (hex (_id), infotype [_info_type]), 3)
#Pos 1-7 Address
_address = (_id & 0b00001111111)
self.log ("Addresse: %d" % _address, 4)
#Determine Addresstype
if ((_address & 0b1000000) == 0b0000000):
self.log ("Terminal %d" % _address, 3)
elif ((_address & 0b1110000) == 0b1000000):
#Obsolete
self.log ("Tuersteuerung obsolete %d" % _address, 3)
elif ((_address & 0b1110000) == 0b1010000):
#Obsolete
self.log ("Tiererkennung obsolete %d" % _address, 3)
elif ((_address & 0b1110000) == 0b1100000):
#Obsolete
self.log ("Wasserplatz obsolete %d" & _address, 3)
elif ((_address & 0b1110000) == 0b1110000):
#Obsolete
self.log ("Other %d" % _address, 1)
_data = msg.data
_repdata = None
_sendecho = 1
#--------------------------------------------Animalnr request-----------------------------------------------------------------
if infotype [_info_type] == 'animal nr':
if 'REQ' in str (_data):
self.log ("Anforderung Tiernummer von Tiererkennung", 3)
#AnimalNr from RFID
animalnr = 0
if int (grfid) > 0:
anr = self.db_con.getanimalnrbyrfid (grfid.lstrip('0'))
if anr:
animalnr = int (anr[0])
#_sendecho = 0
_repdata = bytearray (('REP00%03d' % animalnr).encode ())
self.waitack [_address] = 1
elif self.waitack [_address]:
self.log ("Echo erhalten", 3)
self.waitack [_address] = 0
_sendecho = 0
else:
self.log ("Neue Melkdaten erhalten", 3)
_animalnr = int (_data)
if _animalnr:
if self.dataset [_address]:
self.log ("Es existiert bereits ein Melkdatensatz %s" % self.dataset [_address])
self.dataset [_address] = {'animalnr' : _animalnr,
'ts' : strftime ("%Y-%m-%d %H:%M:%S"),
'ammount' : 0}
else:
self.dataset [_address] = None
self.log ("Tiernummer ist 0 ignoriere Melkdaten")
#---------------------------------------------------------------------------------------------------------------------------
#State requeset
elif infotype [_info_type] == 'state':
_sendecho = 0 #No Echo on State telegram
sdata = _data.decode("utf-8", "ignore")
if self.waitackst [_address]:
self.log ("Echo fuer Status erhalten", 3)
self.waitackst [_address] = 0
_sendecho = 0
elif ('MEL' in sdata) or ('GESP' in sdata) or ('UNBE' in sdata):
self.log ("unerwartetes Echo fuer Status erhalten", 3)
else:
animalnr = int (_data)
self.log ("Statusanfrage fuer Tiernummer %d" % animalnr)
animalinfo = self.db_con.getanimal (animalnr)
milking = self.db_con.getactualmilking ()
_ammountestimated = self.db_con.getestimatedmilkamount (animalnr, milking)
self.log ("Erwartete Milkmenge %d kg fuer Melkung%s" % (_ammountestimated, milking))
if len (animalinfo):
_aianimalnr, _aiearmark, _airfid, _aitsforbidstart, _aitsforbidend = animalinfo[0]
_milkforbid = 0
if _aitsforbidstart:
dttsforbidstart = datetime.strptime (_aitsforbidstart +'00:00:00', '%Y-%m-%d %H:%M:%S')
if datetime.now () > dttsforbidstart:
if _aitsforbidend:
dttsforbidend = datetime.strptime (_aitsforbidend + '23:59:59', '%Y-%m-%d %H:%M:%S')
if datetime.now () <= dttsforbidend:
_milkforbid = 1
else:
_milkforbid = 1
if _milkforbid:
self.log ("Melksperre ist gesetzt" , 1)
_repdata = bytearray ('GESP '.encode ())
self.waitackst [_address] = 1
else:
self.log ("Tier ist Ok checke Melkdaten", 1)
_lastmilkdata = self.db_con.getlastmilkdatabyanimal (animalnr, 1)
if len (_lastmilkdata):
_mdts, _mdanimalnr, _mdammount, _mdtime, _mplace = _lastmilkdata [0]
_mddt = datetime.strptime (_mdts, '%Y-%m-%d %H:%M:%S')
_lmdelta = int ((datetime.now() - _mddt).total_seconds())
self.log ("Letze Melkung war vor %s s" % _lmdelta, 5)
if (_lmdelta < 7200):
self.log ("2te Melkung", 1)
_repdata = bytearray ('2MEL '.encode ())
self.waitackst [_address] = 1
else:
self.log ("Alles Ok", 5)
_repdata = bytearray ((' OK%04d' % int (_ammountestimated)).encode ())
self.waitackst [_address] = 1
else:
self.log ("Alles Ok", 5)
_repdata = bytearray ((' OK%04d' % int (_ammountestimated)).encode ())
self.waitackst [_address] = 1
else:
self.log ("Tier nicht in DB", 1)
_repdata = bytearray ('UNBE '.encode ())
self.waitackst [_address] = 1
elif infotype [_info_type] == 'milkammount':
if self.dataset [_address]:
self.dataset [_address] ['ammount'] = int (_data)
else:
self.log ("Datensatzpuffer leer ignore Milchmenge", 1)
elif infotype [_info_type] == 'milking time':
if self.dataset [_address]:
self.log ("Melkzeit fuer vorhandenen Datensatz im Puffer %s" % self.dataset [_address])
self.dataset [_address] ['time'] = int (_data[:-2]) * 60 + int (_data) % 100
animalinfo = self.db_con.getanimal (self.dataset [_address]['animalnr'])
if len (animalinfo):
self.log ("Tier in DB gefunden", 5)
else:
self.log ("Tier %d nicht in DB gefunden - wird autmatisch angelegt" % (self.dataset [_address]['animalnr']), 1)
#FixMe Check Animal Nr Range + x
_animaldata = {'animalnr' : self.dataset [_address]['animalnr'],
'earmark' : '2730000000000000',
'rfid' : ''}
self.db_con.addanimal (_animaldata)
self.log ("Letzte Melkdaten pruefen", 1)
_lastmilkdata = self.db_con.getlastmilkdatabyanimal (self.dataset [_address]['animalnr'], 1)
if len (_lastmilkdata):
_mdts, _mdanimalnr, _mdammount, _mdtime, _mplace = _lastmilkdata [0]
_mddt = datetime.strptime (_mdts, '%Y-%m-%d %H:%M:%S')
_lmdelta = int ((datetime.now() - _mddt).total_seconds())
self.log ("Letzte Melkung war vor %s s" % _lmdelta, 5)
if _lmdelta < 7200:
self.log ("2te Melkung", 1)
self.log ("Addiere Mlichmenge Neu %d Alt %d, Zeit Neu %dAlt %d" % ( self.dataset [_address] ['ammount'], int (_mdammount), self.dataset [_address] ['time'],int (_mdtime)), 5)
self.dataset [_address] ['ts'] = _mdts
self.dataset [_address] ['ammount'] = self.dataset [_address] ['ammount'] + int (_mdammount)
self.dataset [_address] ['time'] = self.dataset [_address] ['time'] + int (_mdtime)
self.db_con.updatemilkdata (self.dataset [_address])
else:
self.log ("Alles Ok Speichere Melkdaten", 5)
self.db_con.addmilkdata (self.dataset [_address])
else:
self.log ("Alles Ok Speichere Melkdaten", 5)
self.db_con.addmilkdata (self.dataset [_address])
self.dataset [_address] = None
else:
self.log ("Datensatzpuffer leer ignore Melkzeit", 1)
self.log ("ID %s" % hex (_id), 5)
self.log ("data %s" % _data, 5)
self.log ("Sende Quittung/Antwort", 9)
#Preparing Acktelegram
if _sendecho:
nmsg = can.Message (arbitration_id = int (_id & 0b01111111111), data =_data, extended_id = False)
bus.send (nmsg)
self.log ("Quittung: %s" % str (nmsg), 9)
if _repdata:
nmsg = can.Message (arbitration_id = int (_id & 0b01111111111), data =_repdata, extended_id = False)
bus.send (nmsg)
self.log ("Antwort: %s" % str (nmsg), 9)
def is_set (x, n):
"""
Check if Bit i Set 0 ....
"""
return x & 2**n != 0
if __name__ == "__main__":
config = ConfigParser ()
config.read ('lactor.cfg')
c_dbfile = config.get ('Settings', 'dbfile')
if not c_dbfile:
print ('No DB File configured')
sys.exit (0)
c_candev = config.get ('Settings', 'candev')
if not c_candev:
print ('No CAN Device configured')
sys.exit (0)
dbcon = DB (c_dbfile, '/tmp/talog.sql', logtest, errlogtest)
bus = can.interface.Bus (channel = c_candev, bustype = 'socketcan_native')
a_listener = CanListener ()
a_listener.set_db (dbcon)
notifier = can.Notifier (bus, [a_listener])
c_loglevel = int (config.get ('DEBUG', 'loglevel'))
c_herde = config.get ('HERDE', 'herde')
c_herdepath = config.get ('HERDE', 'herdepath').rstrip ('/')
if c_herde and not c_herdepath:
print ('No Path for HERDE Couppling defined')
sys.exit (0)
c_herdepath += '/'
if c_herdepath and not Path (c_herdepath).exists ():
print ('Path to HERDE doesnt exist')
sys.exit (0)
c_herdereadfile = config.get ('HERDE', 'herdereadfile')
if c_herde and not c_herdereadfile:
print ('No Readfile for HERDE Coupplingdefined')
sys.exit (0)
c_herdewritefile = config.get ('HERDE', 'herdewritefile')
if c_herde and not c_herdewritefile:
print ('No Readfile for HERDE Coupplingdefined')
sys.exit (0)
if c_herde:
taurus = TAURUS (dbcon, c_loglevel, c_herdewritefile)
c_animalrecognition = config.get ('Animal Recognition', 'animalrecognition')
if c_animalrecognition:
print ('Animalrecognition configured')
c_rfidip = config.get ('Animal Recognition', 'rfidip')
c_rfidport = int (config.get ('Animal Recognition', 'rfidport'))
print (' with ip %s port %s' % (c_rfidip, c_rfidport))
if not c_rfidip or not c_rfidport:
print ('No RFID Device configured')
sys.exit (0)
"""
Nedap RFID Reader over RS232 / TCP
"""
print ('RFID configured')
try:
srfid = socket (AF_INET, SOCK_STREAM)
srfid.connect ((c_rfidip, c_rfidport))
except:
print ('No connection to RFID')
herdefiletslatch = None
herdechanged = 0
readbuffer = ''
rfidonline = 0
rfidnr = ''
while 1:
sleep (0.01)
if c_herde:
if Path (c_herdepath + c_herdereadfile).exists ():
_herdets = os.stat (c_herdepath + c_herdereadfile).st_mtime
if herdefiletslatch and not herdechanged and herdefiletslatch != _herdets:
print ("New Herdefile wait for changes done")
herdechanged = 1
elif herdefiletslatch and herdechanged and (herdefiletslatch == _herdets) and (time () - _herdets) > 2:
print ("Parse File")
taurus.parsefile (c_herdepath, c_herdereadfile)
herdechanged = 0
herdefiletslatch = _herdets
#Tiererkennung
if c_animalrecognition:
reads = [srfid]
writes = []
(read, writes, xlist) = select (reads, writes, [], 0.01)
for i in read:
data = i.recv (512)
if len (data):
d = data.decode("utf-8", "ignore")
readbuffer += d
if readbuffer [-2:] == '\r\n':
if rfidonline:
data = readbuffer.split (' ')
if len (data) == 3:
newrfid = int (data [2]) % 100000000
if newrfid != rfidnr:
print ("Neue RFID %s" % newrfid)
rfidnr = newrfid
grfid = str (rfidnr)
readbuffer =''
rfidonline = 1
for i in xlist:
print ("Closed Socket")
i.close ()
bus.shutdown ()
sys.exit (0)
try:
foo = 0
except KeyboardInterrupt:
bus.shutdown ()
srfid.close ()