378 lines
14 KiB
Python
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 ()
|