Файл dbc Cantools

Я пытаюсь использовать cantools в Python для декодирования сообщения. В каждом примере, который я вижу, используется файл dbc «tests/files/dbc/motohawk.dbc» или «путь к файлу dbc». Вам необходимо приобрести этот файл или его содержимое? Если нет, то как получить файл, который будет декодировать PGN NMEA2000? В комплект Cantools входит такой?

import cantools
from pprint import pprint

db = cantools.database.load_file('tests/files/dbc/motohawk.dbc')
db.messages
example_message = db.get_message_by_name('ExampleMessage')
pprint(example_message.signals)`

В конечном итоге меня интересует анализ PGN 127501/127502 для стандартной цифровой коммутации NMEA 2000, а не Maretron, CZone или Empribus.

Для освещения я использую контроллер YachtDevices YDSC и релейный модуль Oceanic. Но мне бы хотелось, чтобы приложение управляло вещами рядом с моим картплоттером Garmin.

На сайте cantools (https://pypi.org/project/cantools/) есть много примеров, но все они ссылаются на файлtest/files/dbc/motohawk.dbc, который для меня не существует.

Итак, вопрос двоякий:

  1. где я могу найти файл NMEA 2000 dbc для декодирования данных сообщения?
  2. Как декодировать необработанные данные, если файл dbc недоступен?

Пожалуйста, уточните вашу конкретную проблему или предоставьте дополнительную информацию, чтобы выделить именно то, что вам нужно. Поскольку сейчас написано, трудно точно сказать, о чем вы спрашиваете.

Сергей Кох 11.06.2024 08:20
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
149
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

После разговора с NMEA2000.org я узнал, что их база данных необходима для покупки файла dbc NMEA2000 у стороннего производителя. База данных NMEA2000 стоит очень дорого, в тысячах долларов. Поэтому я решил пойти в другом направлении и расшифровать его самостоятельно.

При использовании Python Can структура сообщений can в библиотеке can работает отлично. Используя этот документ github для структур NMEA2000 PGN, вы можете делать все, что захотите. Github может реализовать проект.

Продолжая отлаживать эту проблему, я смог найти работающее доказательство концепции скрипта Python. Он был разработан на Raspberry Pi 4 B с разъемом PiCAN-M для интерфейса NMEA2000. Вы можете заменить строку инициализации can0 для последовательного порта/USB и использовать ActiSense NGT-1.

Также необходимы графические кнопки или просто измените их на цвета для включения/выключения. Он был разработан с использованием стандартного оборудования NMEA2000, YachtDevices YDSC-04 (для переключателей) и релейного модуля Oceanic Systems (для освещения). Он поддерживает стандартную цифровую коммутацию NMEA2000, а не проприетарные.

Это конкретно касается отправки PGN127501 (состояние переключателя) и PGN127502 (состояние переключателя). Он также декодирует 127502 для обновления состояния переключателя. Интерфейс представляет собой графический графический интерфейс с сенсорным экраном на 8 переключателей. Существует оператор(ы) отладки печати, который печатает все полученные номера PGN, без данных. PGN127501 не декодируется в этом доказательстве концепции, но структура данных такая же, как и у 127502, поэтому для этого существует код.

from tkinter import *
import tkinter.font as fnt
import array as arr 
import can
import time
import os
import threading 
from bitstring import BitArray
from can import Message
            
def button_click(btn):
    if btn == 'b1':       
        if onoff[0]==1:
            button1.config(image=imgon)
            onoff[0]=2
            snd501(b'\xfd\xff') 
            snd502(b'\xfd\xff')        
        else:
            button1.config(image=imgoff)
            onoff[0]=1
            snd501(b'\xfc\xff')
            snd502(b'\xfc\xff')
    elif btn == 'b2':
        if onoff[1]==1:
            button2.config(image=imgon)
            onoff[1]=2
            snd501(b'\xf7\xff')
            snd502(b'\xf7\xff')
        else:
            button2.config(image=imgoff)
            onoff[1]=1
            snd501(b'\xf3\xff')
            snd502(b'\xf3\xff')
    elif btn == 'b3':      
        if onoff[2]==1:
            button3.config(image=imgon)
            onoff[2]=2
            snd501(b'\xdf\xff')  
            snd502(b'\xdf\xff')        
        else:
            button3.config(image=imgoff)
            onoff[2]=1
            snd501(b'\xcf\xff')
            snd502(b'\xcf\xff')
    elif btn == 'b4':
        if onoff[3]==1:
            button4.config(image=imgon)
            onoff[3]=2
            snd501(b'\x7f\xff')
            snd502(b'\x7f\xff')
        else:
            button4.config(image=imgoff)
            onoff[3]=1
            snd501(b'\x3f\xff')
            snd502(b'\x3f\xff')
    elif btn == 'b5':
        if onoff[4]==1:
            button5.config(image=imgon)
            onoff[4]=2
            snd501(b'\xff\xfd')
            snd502(b'\xff\xfd')
        else:
            button5.config(image=imgoff)
            onoff[4]=1
            snd501(b'\xff\xfc')
            snd502(b'\xff\xfc')
    elif btn == 'b6':
        if onoff[5]==1:
            button6.config(image=imgon)
            onoff[5]=2
            snd501(b'\xff\xf7') 
            snd502(b'\xff\xf7') 
        else:     
            button6.config(image=imgoff)
            onoff[5]=1
            snd501(b'\xff\xf3')
            snd502(b'\xff\xf3')
    elif btn == 'b7':
        if onoff[6]==1:
            button7.config(image=imgon)
            onoff[6]=2
            snd501(b'\xff\xdf')
            snd502(b'\xff\xdf')         
        else:
            button7.config(image=imgoff)
            onoff[6]=1
            snd501(b'\xff\xcf')
            snd502(b'\xff\xcf')
    elif btn == 'b8':
        if onoff[7]==1:
            button8.config(image=imgon)
            onoff[7]=2
            snd501(b'\xff\x7f')
            snd502(b'\xff\x7f')
        else:
            button8.config(image=imgoff)
            onoff[7]=1
            snd501(b'\xff\x3f')
            snd502(b'\xff\x3f')

def snd501(BANKSTATUS):
    msg = Message(data=bytearray(BANK.to_bytes(1,'big') + BANKSTATUS + BLANK),
                    arbitration_id=PGN501,
                    dlc=8,
                    channel='can0',
                    timestamp=time.time(),
                    is_extended_id=True)
    bus.send(msg)
    print('PGN SENT: ' + PGN501 + ' ID: ' + str(msg.arbitration_id) + ' Data: ' + str(msg.data ))

def snd502(BANKSTATUS):
    msg = Message(data=bytearray(BANK.to_bytes(1,'big') + BANKSTATUS + BLANK),
                    arbitration_id=PGN502,
                    dlc=8,
                    channel='can0',
                    timestamp=time.time(),
                    is_extended_id=True)
    bus.send(msg)
    print('PGN SENT: ' + PGN502 + ' ID: ' + str(msg.arbitration_id) + ' Data: ' + str(msg.data ))

def getpgn(pgn): 
    pgn=bin(pgn) #Get bin of the id
    pgn = pgn[2:len(pgn)] # strip off the '0b'
    while len(pgn) < 29 : # make sure it's 29 bits
        pgn = '0' + pgn 
    pgn = pgn[3:-8] #get the middle 18 bits
    pgn = int(pgn, 2) #convert to int
    return pgn

def handle_data(PGN, data):
    if PGN == 127502:
        msgData = data
        if getBank(msgData) == BANK:
            bankstatus = str(data)
            # print(bankstatus[18:-26])
            # print(bankstatus[22:-22])
            bankstatus1 = bankstatus[18:-26]
            SetBank1Value(bankstatus1)
            bankstatus2 = bankstatus[22:-22]
            SetBank2Value(bankstatus2)
    if PGN == 127501:
        msgData = data
        if getBank(msgData) == BANK:
            bankstatus = str(data)
            print(bank status)

def SetBank1Value(bankstatus):
    if bankstatus != 'ff':
        if bankstatus == 'fd':
            button1.config(image=imgon)
            onoff[0]=2
        elif bankstatus == 'fc':
            button1.config(image=imgoff)
            onoff[0]=1
        elif bankstatus == 'f7':
            button2.config(image=imgon)
            onoff[1]=2
        elif bankstatus == 'f3':
            button2.config(image=imgoff)
            onoff[1]=1               
        elif bankstatus == 'df':
            button3.config(image=imgon)
            onoff[2]=2
        elif bankstatus == 'cf':
            button3.config(image=imgoff)
            onoff[2]=1
        elif bankstatus == '7f':
            button3.config(image=imgon)
            onoff[3]=2
        elif bankstatus == '3f':
            button3.config(image=imgoff)
            onoff[3]=1
    
def SetBank2Value(bankstatus):
    if bankstatus != 'ff':
        if bankstatus == 'fd':
            button5.config(image=imgon)
            onoff[4]=2
        elif bankstatus == 'fc':
            button5.config(image=imgoff)
            onoff[4]=1
        elif bankstatus == 'f7':
            button6.config(image=imgon)
            onoff[5]=2
        elif bankstatus == 'f3':
            button6.config(image=imgoff)
            onoff[5]=1               
        elif bankstatus == 'df':
            button7.config(image=imgon)
            onoff[6]=2
        elif bankstatus == 'cf':
            button7.config(image=imgoff)
            onoff[6]=1
        elif bankstatus == '7f':
            button8.config(image=imgon)
            onoff[7]=2
        elif bankstatus == '3f':
            button8.config(image=imgoff)
            onoff[7]=1

def getBank(data):
    try:
        bank = str(data)
        bank = bank[13:-30]
        bank = int('0' + bank, 0)
        return bank
    except:
        return 999 #return an non exisant number

def read_from_port():
    connected = False
    try:
        while not connected:
            message = bus.recv()
            for msg in bus:
                PGN = getpgn(msg.arbitration_id)
                if PGN == 127501 or PGN == 127502 : 
                    print(str(PGN) + " : " + str(msg.arbitration_id) + " : " + str(msg.data ))
                    handle_data(PGN, msg.data)
                else:
                    print(str(PGN) + " : " + str(msg.arbitration_id) + " : " + str(msg.data ))
    except:
        exit()

    connected = True

def exit_app():
        connected = True
        bus.shutdown()
        time.sleep(0.3)
        os._exit
        time.sleep(0.1)
        app.destroy()
        exit()
    
app = Tk() 

# needed to be able to shutdown port
connected = False

PGN501 = 233966978
PGN502 = 233967275

# Switch Bank var's
BANK = 12 # BANK.to_bytes(1,'big') 
BLANK = bytearray(b'\xff\xff\xff\xff\xff')

# Switches 1-->8
#lower nibble of first byte is 2,1 upper nibble is 4,3
#lower nibble of 2nd byte is 6,5 upper nibble is 8,7
#the mask to leave a byte unchanged is ff or f if a nibble.
# BANKSTATUS = bytearray(b'\xff\xff')

os.system("sudo /sbin/ip link set can0 up type can bitrate 500000")
time.sleep(0.1) 

app.title("Reel Nauti Switching") 
#app.geometry("1920x1080") #Window dimensions

app.geometry("1200x700") #Window dimensions
app.configure(background='black')

#image size is 202x315 XxY
imgblank = PhotoImage(file = r"./images/blank.png")
imgon = PhotoImage(file = r"./images/on.png")
imgoff = PhotoImage(file = r"./images/off.png")

spacer = 20
btnw = 202
btnh = 315

PGN = any

# define new font
newfont = fnt.Font(family='Arial', size=22, weight=fnt.BOLD)

# internal button state array 0=uninitated, 1=off, 2=on
onoff = arr.array('i',[0,0,0,0,0,0,0,0]) # array starts at 0 using 0-7

# Create button 1
button1 = Button(app, text = "Engine\nRoom\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
                 command=lambda m = "b1":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
                 activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0)
button1.pack() 
button1.place(x=spacer , y=spacer)

# Create button 2
button2 = Button(app, text = "Courtesy\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
                 command=lambda m = "b2":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
                 activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button2.pack() 
button2.place(x=btnw + (2 * spacer) , y=spacer)

# Create button 3
button3 = Button(app, text = "Spreader\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
                 command=lambda m = "b3":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
                 activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button3.pack() 
button3.place(x=(2 *btnw) + (3 * spacer) , y=spacer)

# Create button 4
button4 = Button(app, text = "Under\nWater\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
                 command=lambda m = "b4":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
                 activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button4.pack() 
button4.place(x=(3 * btnw) + (4 * spacer) , y=spacer)

# Create button 5 on second row
button5 = Button(app, text = "Arch\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
                 command=lambda m = "b5":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
                 activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button5.pack() 
button5.place(x=spacer, y=btnh + (spacer * 2))

# Create button 6 on second row
button6 = Button(app, text = "Dash\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
                 command=lambda m = "b6":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
                 activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button6.pack() 
button6.place(x= btnw + (2 * spacer) , y=btnh + (spacer * 2))

# Create button 7 on second row
button7 = Button(app, text = "Cockpit\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
                 command=lambda m = "b7":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
                 activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button7.pack() 
button7.place(x= (2 * btnw) + (3 * spacer) , y=btnh + (spacer * 2))

# Create button 8 on second row
button8 = Button(app, text = "Salon\nHi-Hats", image=imgblank, borderwidth=0, font=newfont, compound = "center",
                 command=lambda m = "b8":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
                 activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button8.pack() 
button8.place(x= (3 * btnw) + (4 * spacer) , y=btnh + (spacer * 2))

# Create exit button - where to put it?
button9 = Button(app, text = "Exit", command=exit_app)
button9.pack() 
button9.place(x= (4 * btnw) + (5 * spacer) , y=btnh + (spacer * 2))

connected = False

try:
    bus = can.interface.Bus(channel='can0', bustype='socketcan')
    print('Ready')
except:
    print('Init of PICAN failed')

# thread to handle the can port
thread = threading.Thread(target=read_from_port)
thread.start()

# Mainloop that will run forever 
app.mainloop()

Другие вопросы по теме