chore: migrate

This commit is contained in:
anjingyu 2024-04-14 23:20:38 +08:00
parent f2c6c4935a
commit 63930a3d9b
805 changed files with 145362 additions and 0 deletions

137
.gitignore vendored Normal file
View File

@ -0,0 +1,137 @@
.build/
bin/
lib/
Cargo.lock
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

208
LICENSE Normal file
View File

@ -0,0 +1,208 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution
as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct
or indirect, to cause the direction or management of such entity, whether
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation
or translation of a Source form, including but not limited to compiled object
code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form,
made available under the License, as indicated by a copyright notice that
is included in or attached to the work (an example is provided in the Appendix
below).
"Derivative Works" shall mean any work, whether in Source or Object form,
that is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative
Works shall not include works that remain separable from, or merely link (or
bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative
Works thereof, that is intentionally submitted to Licensor for inclusion in
the Work by the copyright owner or by an individual or Legal Entity authorized
to submit on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication
sent to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor
for the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently incorporated
within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
Derivative Works of, publicly display, publicly perform, sublicense, and distribute
the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License,
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section) patent
license to make, have made, use, offer to sell, sell, import, and otherwise
transfer the Work, where such license applies only to those patent claims
licensable by such Contributor that are necessarily infringed by their Contribution(s)
alone or by combination of their Contribution(s) with the Work to which such
Contribution(s) was submitted. If You institute patent litigation against
any entity (including a cross-claim or counterclaim in a lawsuit) alleging
that the Work or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses granted to You
under this License for that Work shall terminate as of the date such litigation
is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and
in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy
of this License; and
(b) You must cause any modified files to carry prominent notices stating that
You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source
form of the Work, excluding those notices that do not pertain to any part
of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution,
then any Derivative Works that You distribute must include a readable copy
of the attribution notices contained within such NOTICE file, excluding those
notices that do not pertain to any part of the Derivative Works, in at least
one of the following places: within a NOTICE text file distributed as part
of the Derivative Works; within the Source form or documentation, if provided
along with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works
that You distribute, alongside or as an addendum to the NOTICE text from the
Work, provided that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction,
or distribution of Your modifications, or for any such Derivative Works as
a whole, provided Your use, reproduction, and distribution of the Work otherwise
complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without
any additional terms or conditions. Notwithstanding the above, nothing herein
shall supersede or modify the terms of any separate license agreement you
may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
in writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
of using or redistributing the Work and assume any risks associated with Your
exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether
in tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to
in writing, shall any Contributor be liable to You for damages, including
any direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability
to use the Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all other commercial
damages or losses), even if such Contributor has been advised of the possibility
of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work
or Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such obligations,
You may act only on Your own behalf and on Your sole responsibility, not on
behalf of any other Contributor, and only if You agree to indemnify, defend,
and hold each Contributor harmless for any liability incurred by, or claims
asserted against, such Contributor by reason of your accepting any such warranty
or additional liability. END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own identifying
information. (Don't include the brackets!) The text should be enclosed in
the appropriate comment syntax for the file format. We also recommend that
a file or class name and description of purpose be included on the same "printed
page" as the copyright notice for easier identification within third-party
archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,2 +1,3 @@
# toys
Powerful or useful scripts.

205
aes/AES.java Normal file
View File

@ -0,0 +1,205 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.File;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Scanner;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
//import sun.misc.BASE64Decoder;
//import sun.misc.BASE64Encoder;
//import RC4;
public class AES {
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
public static String AES_Encode(String encodeRules, String content) {
try {
// 1.构造密钥生成器指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance("AES");
// 2.根据ecnodeRules规则初始化密钥生成器
// 生成一个128位的随机源,根据传入的字节数组
// keygen.init(128, new SecureRandom(encodeRules.getBytes()));
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(encodeRules.getBytes());
keygen.init(128, secureRandom);
// 3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
// 4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
// 5.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(raw, "AES");
// 6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance("AES");
// 7.初始化密码器第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key);
// 8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byte_encode = content.getBytes("utf-8");
// 9.根据密码器的初始化方式--加密将数据加密
byte[] byte_AES = cipher.doFinal(byte_encode);
// 10.将加密后的数据转换为字符串
// 这里用Base64Encoder中会找不到包
// 解决办法
// 在项目的Build path中先移除JRE System Library再添加库JRE System Library重新编译后就一切正常了
// String AES_encode=new String(new BASE64Encoder().encode(byte_AES));
String AES_encode = new String(bytesToHexString(byte_AES));
// 11.将字符串返回
return AES_encode;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 如果有错就返加nulll
return null;
}
/*
* 解密
* 解密过程
* 1.同加密1-4步
* 2.将加密后的字符串反纺成byte[]数组
* 3.将加密内容解密
*/
public static String AES_Decode(String encodeRules, String content) {
String ss;
try {
// 1.构造密钥生成器指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance("AES");
// 2.根据ecnodeRules规则初始化密钥生成器
// 生成一个128位的随机源,根据传入的字节数组
// keygen.init(128, new SecureRandom(encodeRules.getBytes()));
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(encodeRules.getBytes());
keygen.init(128, secureRandom);
// 3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
// 4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
// 5.根据字节数组生成AES密钥
SecretKey key = new SecretKeySpec(raw, "AES");
// 6.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance("AES");
// 7.初始化密码器第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key);
// 8.将加密并编码后的内容解码成字节数组
byte[] byte_content = hexStringToBytes(content);
// byte [] byte_content= new BASE64Decoder().decodeBuffer(content);
// System.out.println("ttttttttttttttttttttttttttt");
// System.out.println(new String(byte_content, "UTF-8"));
/*
* 解密
*/
byte[] byte_decode = cipher.doFinal(byte_content);
String AES_decode = new String(byte_decode, "utf-8");
return AES_decode;
} catch (NoSuchAlgorithmException e) {
ss = "NoSuchAlgorithmException";
e.printStackTrace();
} catch (NoSuchPaddingException e) {
ss = "NoSuchPaddingException";
e.printStackTrace();
} catch (InvalidKeyException e) {
ss = "InvalidKeyException";
e.printStackTrace();
} catch (IOException e) {
ss = "IOException";
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
ss = "IllegalBlockSizeException";
e.printStackTrace();
} catch (BadPaddingException e) {
ss = "BadPaddingException";
e.printStackTrace();
}
// 如果有错就返加nulll
return ss;
}
public static void main(String[] args) {
// MyClass se=new MyClass();
// Scanner scanner=new Scanner(System.in);
/*
* 加密
*/
System.out.println("使用AES对称加密请输入加密的规则");
// String encodeRules=scanner.next();
System.out.println("请输入要加密的内容:");
String encodeRules = "a02254eb247146dfa446c4b2795ebf90";
String content = "32770";
String outstr = AES_Encode(encodeRules, content);
System.out.println("根据输入的规则" + encodeRules + "加密后的密文是:" + outstr);
/*
* 解密
*/
System.out.println("使用AES对称解密请输入加密的规则(须与加密相同)");
String byte_str = "0212f41b372fe457c3f7df0757151615";
outstr = AES_Decode(encodeRules, byte_str);
System.out.println("根据输入的规则" + encodeRules + "解密后的明文是:" + outstr);
}
}

91
aes/aes.py Normal file
View File

@ -0,0 +1,91 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from base64 import b64encode, encodebytes
from Crypto.Cipher import AES
import binascii
import hashlib
BS = AES.block_size
def padding_pkcs5(value):
return str.encode(value +
(BS - len(value) % BS) * chr(BS - len(value) % BS))
def padding_zero(value):
while len(value) % 16 != 0:
value += '\0'
return str.encode(value)
def aes_ecb_encrypt(key, value):
''' AES/ECB/NoPadding encrypt '''
key = bytes.fromhex(key)
cryptor = AES.new(key, AES.MODE_ECB)
ciphertext = cryptor.encrypt(bytes.fromhex(value))
return ''.join(['%02x' % i for i in ciphertext]).upper()
def aes_ecb_decrypt(key: str, value: str) -> str:
''' AES/ECB/NoPadding decrypt '''
key = bytes.fromhex(key)
cryptor = AES.new(key, AES.MODE_ECB)
ciphertext = cryptor.decrypt(bytes.fromhex(value))
return ''.join(['%02x' % i for i in ciphertext]).upper()
def get_userkey(key, value):
''' AES/ECB/PKCS5Padding encrypt
就是一个普通的AES ECB加密填充方式是PKCS5Padding
'''
cryptor = AES.new(bytes.fromhex(key), AES.MODE_ECB)
padding_value = padding_pkcs5(value)
ciphertext = cryptor.encrypt(padding_value)
return ''.join(['%02x' % i for i in ciphertext]).upper()
def get_sha1prng_key(key):
'''[summary]
encrypt key with SHA1PRNG
same as java AES crypto key generator SHA1PRNG
Arguments:
key {[string]} -- [key]
Returns:
[string] -- [hexstring]
'''
signature = hashlib.sha1(key.encode()).digest()
signature = hashlib.sha1(signature).digest()
return ''.join(['%02x' % i for i in signature]).upper()[:32]
if __name__ == '__main__':
# 普通AES ECB加解密
data = '0513011E0005016400000000000000000001000000000000000000000000000000770013011E0005026400000000000000000001000000000000000000000000000000770013011E0005036400000000000000000001000000000000000000000000000000770013011E0005046400000000000000000001000000000000000000000000000000770013011E000505640000000000000000000100000000000000000000000000000077000000000000' # 原文
key = '43C8B53E236C4756B8FF24E5AA08A549' #加密key
aes_result = 'AB4F4686218A5FF9F07E5248E6B5525D140602A0FAA21176C9A158A010B1A7C0258E80667BF7DD3B6FF57707B373BF75F57AE634D9F1384002AA6B788F4C658DD77572C207AAE3134F91FB690A4F024EF428DE3E1C5F84D0EA9D01B8AB4ED9FE97D7C0D65D447D92F0E306573F30E1360B3DE999E952BAAB9B22E48B8C7B23DC5480027DEE44988F0E86F7A475EEF599C1D7D3331457E582558BC3447E644913ABD63FC221C2E0D49BD712879261FF5F' #加密结果
# 加密
aes128string = aes_ecb_encrypt(key, data)
print(aes128string)
# 解密
aes128string = aes_ecb_decrypt(key, aes_result)
print(aes128string)
# sha1prng 加密方式
mac = '405EE11002F3' #原文
device_id = '12532802' #'12403492' #'12532802' # 加密key
user_key = 'c1ee1f3f2d74e02706be9af78aa79ba4'.upper() # 加密结果
# 加密
# 1.对key 进行sha1prng加密 get_sha1prng_key
# 2.用加密后的key进行AES AES/ECB/PKCS5Padding 加密,
# 注意看你的java代码AES加密方式填充方式我这里是ECB加密填充方式是PKCS5Padding
# get_userkey 方法就是普通的 AES/ECB/PKCS5Padding 加密
# https://blog.csdn.net/max229max/article/details/70318782 这篇有AES的ECB和CBC加密
aes128string = get_userkey(get_sha1prng_key(device_id), mac)
print(aes128string)
# 58F7CD929BFAA0915032536FBA8D3281420E93E3
# 58f7cd929bfaa0915032536fba8d3281
# 58F7CD929BFAA0915032536FBA8D3281
print(get_sha1prng_key('12532802')) # 58f7cd929bfaa0915032536fba8d3281

305
async/aioudp.py Normal file
View File

@ -0,0 +1,305 @@
"""Provide high-level UDP endpoints for asyncio.
Example:
async def main():
# Create a local UDP enpoint
local = await open_local_endpoint('localhost', 8888)
# Create a remote UDP enpoint, pointing to the first one
remote = await open_remote_endpoint(*local.address)
# The remote endpoint sends a datagram
remote.send(b'Hey Hey, My My')
# The local endpoint receives the datagram, along with the address
data, address = await local.receive()
# This prints: Got 'Hey Hey, My My' from 127.0.0.1 port 8888
print(f"Got {data!r} from {address[0]} port {address[1]}")
"""
__all__ = ['open_local_endpoint', 'open_remote_endpoint']
# Imports
import asyncio
import warnings
# Datagram protocol
class DatagramEndpointProtocol(asyncio.DatagramProtocol):
"""Datagram protocol for the endpoint high-level interface."""
def __init__(self, endpoint):
self._endpoint = endpoint
# Protocol methods
def connection_made(self, transport):
self._endpoint._transport = transport
def connection_lost(self, exc):
assert exc is None
if self._endpoint._write_ready_future is not None:
self._endpoint._write_ready_future.set_result(None)
self._endpoint.close()
# Datagram protocol methods
def datagram_received(self, data, addr):
self._endpoint.feed_datagram(data, addr)
def error_received(self, exc):
msg = 'Endpoint received an error: {!r}'
warnings.warn(msg.format(exc))
# Workflow control
def pause_writing(self):
assert self._endpoint._write_ready_future is None
loop = self._endpoint._transport._loop
self._endpoint._write_ready_future = loop.create_future()
def resume_writing(self):
assert self._endpoint._write_ready_future is not None
self._endpoint._write_ready_future.set_result(None)
self._endpoint._write_ready_future = None
# Enpoint classes
class Endpoint:
"""High-level interface for UDP enpoints.
Can either be local or remote.
It is initialized with an optional queue size for the incoming datagrams.
"""
def __init__(self, queue_size=None):
if queue_size is None:
queue_size = 0
self._queue = asyncio.Queue(queue_size)
self._closed = False
self._transport = None
self._write_ready_future = None
# Protocol callbacks
def feed_datagram(self, data, addr):
try:
self._queue.put_nowait((data, addr))
except asyncio.QueueFull:
warnings.warn('Endpoint queue is full')
def close(self):
# Manage flag
if self._closed:
return
self._closed = True
# Wake up
if self._queue.empty():
self.feed_datagram(None, None)
# Close transport
if self._transport:
self._transport.close()
# User methods
def send(self, data, addr):
"""Send a datagram to the given address."""
if self._closed:
raise IOError("Enpoint is closed")
self._transport.sendto(data, addr)
async def receive(self):
"""Wait for an incoming datagram and return it with
the corresponding address.
This method is a coroutine.
"""
if self._queue.empty() and self._closed:
raise IOError("Enpoint is closed")
data, addr = await self._queue.get()
if data is None:
raise IOError("Enpoint is closed")
return data, addr
def abort(self):
"""Close the transport immediately."""
if self._closed:
raise IOError("Enpoint is closed")
self._transport.abort()
self.close()
async def drain(self):
"""Drain the transport buffer below the low-water mark."""
if self._write_ready_future is not None:
await self._write_ready_future
# Properties
@property
def address(self):
"""The endpoint address as a (host, port) tuple."""
return self._transport.get_extra_info("socket").getsockname()
@property
def closed(self):
"""Indicates whether the endpoint is closed or not."""
return self._closed
class LocalEndpoint(Endpoint):
"""High-level interface for UDP local enpoints.
It is initialized with an optional queue size for the incoming datagrams.
"""
pass
class RemoteEndpoint(Endpoint):
"""High-level interface for UDP remote enpoints.
It is initialized with an optional queue size for the incoming datagrams.
"""
def send(self, data):
"""Send a datagram to the remote host."""
super().send(data, None)
async def receive(self):
""" Wait for an incoming datagram from the remote host.
This method is a coroutine.
"""
data, addr = await super().receive()
return data
# High-level coroutines
async def open_datagram_endpoint(
host, port, *, endpoint_factory=Endpoint, remote=False, **kwargs):
"""Open and return a datagram endpoint.
The default endpoint factory is the Endpoint class.
The endpoint can be made local or remote using the remote argument.
Extra keyword arguments are forwarded to `loop.create_datagram_endpoint`.
"""
loop = asyncio.get_event_loop()
endpoint = endpoint_factory()
kwargs['remote_addr' if remote else 'local_addr'] = host, port
kwargs['protocol_factory'] = lambda: DatagramEndpointProtocol(endpoint)
await loop.create_datagram_endpoint(**kwargs)
return endpoint
async def open_local_endpoint(
host='0.0.0.0', port=0, *, queue_size=None, **kwargs):
"""Open and return a local datagram endpoint.
An optional queue size arguement can be provided.
Extra keyword arguments are forwarded to `loop.create_datagram_endpoint`.
"""
return await open_datagram_endpoint(
host, port, remote=False,
endpoint_factory=lambda: LocalEndpoint(queue_size),
**kwargs)
async def open_remote_endpoint(
host, port, *, queue_size=None, **kwargs):
"""Open and return a remote datagram endpoint.
An optional queue size arguement can be provided.
Extra keyword arguments are forwarded to `loop.create_datagram_endpoint`.
"""
return await open_datagram_endpoint(
host, port, remote=True,
endpoint_factory=lambda: RemoteEndpoint(queue_size),
**kwargs)
# Testing
try:
import pytest
pytestmark = pytest.mark.asyncio
except ImportError: # pragma: no cover
pass
async def test_standard_behavior():
local = await open_local_endpoint()
remote = await open_remote_endpoint(*local.address)
remote.send(b'Hey Hey')
data, address = await local.receive()
assert data == b'Hey Hey'
assert address == remote.address
local.send(b'My My', address)
data = await remote.receive()
assert data == b'My My'
local.abort()
assert local.closed
with pytest.warns(UserWarning):
await asyncio.sleep(1e-3)
remote.send(b'U there?')
await asyncio.sleep(1e-3)
remote.abort()
assert remote.closed
async def test_closed_endpoint():
local = await open_local_endpoint()
future = asyncio.ensure_future(local.receive())
local.abort()
assert local.closed
with pytest.raises(IOError):
await future
with pytest.raises(IOError):
await local.receive()
with pytest.raises(IOError):
await local.send(b'test', ('localhost', 8888))
with pytest.raises(IOError):
local.abort()
async def test_queue_size():
local = await open_local_endpoint(queue_size=1)
remote = await open_remote_endpoint(*local.address)
remote.send(b'1')
remote.send(b'2')
with pytest.warns(UserWarning):
await asyncio.sleep(1e-3)
assert await local.receive() == (b'1', remote.address)
remote.send(b'3')
assert await local.receive() == (b'3', remote.address)
remote.send(b'4')
await asyncio.sleep(1e-3)
local.abort()
assert local.closed
assert await local.receive() == (b'4', remote.address)
remote.abort()
assert remote.closed
async def test_flow_control():
m = n = 1024
remote = await open_remote_endpoint("8.8.8.8", 12345)
for _ in range(m):
remote.send(b"a" * n)
await remote.drain()
for _ in range(m):
remote.send(b"a" * n)
remote.abort()
await remote.drain()
if __name__ == '__main__': # pragma: no cover
pytest.main([__file__])

View File

@ -0,0 +1,95 @@
#!/usr/bin/env python3
# -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8
import sys
import asyncio
import time
import threading
class AsyncSocketService:
def __init__(self, address='127.0.0.1', port=5000,
bufsize: int = 10240,
required_pkg_size: int = 0, debug: bool=False):
self.__addr = address
self.__port = port
self.__bufsize = bufsize
self.__required_pkg_size = required_pkg_size
self.__debug = debug
async def handle_accept(self, reader, writer):
while True:
async def read_all():
cached_data = bytes()
while True:
cdata = await reader.read(self.__bufsize)
if len(cdata) <= 0:
break
cached_data += cdata
if cached_data == b'exit':
break
if self.__required_pkg_size > len(cached_data):
continue
else:
break
return cached_data
data = await read_all()
if not data or data == b'exit':
break
if self.__debug:
client = writer.get_extra_info('peername')
print('Received from {}: {!r}'.format(client, data))
# Do process
writer.write(data)
await writer.drain()
print('Close the client socket')
writer.close()
self.__loop.call_soon_threadsafe(self.__loop.stop)
def run_service(self, loop: asyncio.BaseEventLoop):
self.__loop = loop
asyncio.set_event_loop(loop)
server_coro = asyncio.start_server(self.handle_accept, self.__addr, self.__port,
loop=loop)
server = loop.run_until_complete(server_coro)
host = server.sockets[0].getsockname()
print('Serving on {}. Hit CTRL-C to stop.'.format(host))
try:
loop.run_forever()
except KeyboardInterrupt:
print("CTRL+C")
print('Server shutting down.')
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
def start_service(self):
start_msg = self.start_msg()
loop = asyncio.new_event_loop()
t = threading.Thread(target=self.run_service, args=(loop,))
t.start()
if __name__ == '__main__':
s1 = AsyncSocketService(address='127.0.0.1', port=5000, required_pkg_size=100)
s2 = AsyncSocketService(address='127.0.0.1', port=6000, required_pkg_size=100)
s1.start_service()
s2.start_service()
while True:
try:
time.sleep(1.0)
except:
break

299
async/timer_task.py Normal file
View File

@ -0,0 +1,299 @@
#!/usr/bin/env python3
# -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8
import asyncio
import time
import threading
import concurrent
from typing import List
import signal
import functools
__all__ = ['TimerTaskManager', 'TimerTask']
__author__ = ['"donkey" <anjingyu_ws@foxmail.com>']
"""The TimerTask class is an abstract helper tool used to create tasks that are executed periodically(like a timer) in the asynchronous way.
Although the performance is higher than multithreading in most cases, but if there are too many timers in one thread, the performance will be severely degraded.
Typical usage example:
class MyTask(TimerTask):
def __init__(self, interval):
# Also, you can provide your own loop
super().__init__(interval=interval)
# Maybe in non-main thread, according to the looper
async def callback(self):
print("Do my working")
# may sleep for a while
await asyncio.sleep(0.1)
"""
import time
import asyncio
import uuid
from threading import Thread, current_thread
class TimerTaskLoop:
def __init__(self, interval, loop=asyncio.get_event_loop()):
self.__loop = loop
self.__interval = interval
self.__name = "{}+{}".format(self.__class__.__name__, str(uuid.uuid4()))
self.__stop = False
self.__task = asyncio.run_coroutine_threadsafe(self.__job(), loop=self.__loop)
async def callback(self):
pass
async def __job(self):
delay_time = self.__interval
try:
while not self.__stop:
ts = time.time()
await self.callback()
delay_time = self.__interval - (time.time() - ts)
await asyncio.sleep(delay_time, loop=self.__loop)
except Exception as ex:
print(ex)
@property
def name(self) -> str:
return self.__name
@name.setter
def name(self, value):
self.__name = value
def cancel(self):
self.__stop = True
self.__task.cancel()
return self.__task
class TimerTask:
def __init__(self, interval):
self.__interval = interval
self.__name = "{}+{}".format(self.__class__.__name__, str(uuid.uuid4()))
async def callback(self):
pass
def before_start(self):
"""Callback before the timer start."""
pass
def after_end(self):
"""After the timer destory"""
pass
@property
def interval(self) -> float:
return self.__interval
@property
def name(self) -> str:
return self.__name
@name.setter
def name(self, value):
self.__name = value
class TimerTaskManager:
def __init__(self, in_thread=False):
if threading.current_thread() is not threading.main_thread():
raise RuntimeError("TimerTaskManager can only be created in Main Thread!")
if in_thread:
self.__loop = asyncio.new_event_loop()
self.__thread = threading.Thread(target=self.__run, args=(self.__loop, ))
else:
self.__loop = asyncio.get_event_loop()
self.__thread = None
self.__timers = {}
self.__stop = False
def start(self):
self.__stop = False
if self.__thread:
self.__thread.start()
def stop(self):
self.__stop = True
if self.__thread:
for timer in self.__timers.values():
timer.cancel()
if len(self.__timers):
# Avoid WARNING: "Task was destroyed but it is pending!"
concurrent.futures.wait(self.__timers.values())
self.__timers = {}
self.__loop.call_soon_threadsafe(self.__loop.stop)
self.__thread.join()
self.__thread = None
def __run(self, loop):
print("[DEBUG] Run in thread:", threading.current_thread())
try:
loop.run_forever()
except KeyboardInterrupt:
print("CTRL+C")
loop.close()
async def __job(self, task):
delay_time = task.interval
try:
task.before_start()
while not self.__stop:
ts = time.time()
await task.callback()
delay_time = task.interval - (time.time() - ts)
await asyncio.sleep(delay_time, loop=self.__loop)
except Exception as ex:
print(ex)
finally:
task.after_end()
def add_timer(self, *tasks: List[TimerTask]) -> List[str]:
ret = []
for task in tasks:
assert task.name not in self.__timers
self.__timers[task.name] = asyncio.run_coroutine_threadsafe(self.__job(task), loop=self.__loop)
ret.append(task.name)
return ret
def remove_timer(self, *timer_ids: List[str]):
for timer_id in timer_ids:
if timer_id in self.__timers:
self.__timers[timer_id].cancel()
del self.__timers[timer_id]
if len(self.__timers) == 0:
self.__loop.call_soon_threadsafe(self.__loop.stop)
def test_pure_task():
class MyTimer(TimerTaskLoop):
def __init__(self, interval, loop: asyncio.AbstractEventLoop=asyncio.get_event_loop()):
# Also, you can provide your own loop
super().__init__(interval=interval, loop=loop)
self.__counter = 0
# Maybe in non-main thread, according to the looper
async def callback(self):
print("Do my working: " + self.name + f"( + {time.time()} + ), count: {self.__counter}")
# may sleep for a while
await asyncio.sleep(0.1)
self.__counter += 1
@property
def counter(self):
return self.__counter
def run_service(loop, my_timer):
try:
my_timer.name = "{}[{}]".format(my_timer.name, threading.current_thread().ident)
loop.run_forever()
except KeyboardInterrupt:
print("CTRL+C")
main_loop = asyncio.get_event_loop()
loop = asyncio.new_event_loop()
def stopper(signame, loop):
print("Got %s, stopping..." % signame)
loop.stop()
# Not available on Windows
# for signame in ('SIGINT', 'SIGTERM'):
# loop.add_signal_handler(getattr(signal, signame), functools.partial(stopper, signame, loop))
# main_loop.add_signal_handler(getattr(signal, signame), functools.partial(stopper, signame, main_loop))
my_timer1 = MyTimer(interval=0.22)
my_timer2 = MyTimer(interval=0.22, loop=loop)
my_timer1.name = "{}[{}]".format(my_timer1.name, threading.currentThread().ident)
t = threading.Thread(target=run_service, args=(loop, my_timer2, ))
t.start()
time.sleep(2.0)
my_timer3 = MyTimer(interval=0.3, loop=loop)
async def stop_tm():
await asyncio.sleep(3.0)
t1 = my_timer1.cancel()
t2 = my_timer2.cancel()
t3 = my_timer3.cancel()
concurrent.futures.wait([t2, t3])
loop.call_soon_threadsafe(loop.stop)
# We should join the thread
await asyncio.sleep(0.1)
asyncio.get_event_loop().stop()
asyncio.ensure_future(stop_tm())
try:
main_loop.run_forever()
except:
loop.call_soon_threadsafe(loop.stop)
finally:
main_loop.close()
print("counter:", my_timer1.counter)
print("counter:", my_timer2.counter)
print("counter:", my_timer3.counter)
def test_mgr():
class MyNewTimer(TimerTask):
def __init__(self, interval):
# Also, you can provide your own loop
super().__init__(interval=interval)
self.__counter = 0
# Maybe in non-main thread, according to the looper
async def callback(self):
print("Do my working: " + self.name + f"( + {time.time()} + ), count: {self.__counter}")
# may sleep for a while
await asyncio.sleep(0.1)
self.__counter += 1
@property
def counter(self):
return self.__counter
in_thread = True
# in_thread = True
ttm = TimerTaskManager(in_thread)
t1 = MyNewTimer(interval=0.22)
t2 = MyNewTimer(interval=0.3)
tid1, tid2 = ttm.add_timer(t1, t2)
ttm.start()
async def stop_tm():
await asyncio.sleep(5.0)
ttm.remove_timer(tid1, tid2)
ttm.stop()
asyncio.get_event_loop().stop()
asyncio.ensure_future(stop_tm())
try:
asyncio.get_event_loop().run_forever()
except KeyboardInterrupt:
print("CTRL+C")
ttm.stop()
if __name__ == '__main__':
test_pure_task()
# test_mgr()

95
async/udp_server.py Normal file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python3
# -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8
import sys
import asyncio
class EchoServerProtocol:
def connection_made(self, transport):
self.transport = transport
def datagram_received(self, data, addr):
message = data.decode()
print('Received %r from %s' % (message, addr))
print('Send %r to %s' % (message, addr))
self.transport.sendto(data, addr)
#def datagram_received(self, data, addr):
# loop = asyncio.get_event_loop()
# loop.create_task(self.handle_income_packet(data, addr))
#async def handle_income_packet(self, data, addr):
# # echo back the message, but 2 seconds later
# await asyncio.sleep(2)
# self.transport.sendto(data, addr)
async def server_main():
print("Starting UDP server")
# Get a reference to the event loop as we plan to use
# low-level APIs.
loop = asyncio.get_running_loop()
# One protocol instance will be created to serve all
# client requests.
transport, protocol = await loop.create_datagram_endpoint(
lambda: EchoServerProtocol(),
local_addr=('127.0.0.1', 9999))
try:
await asyncio.sleep(3600) # Serve for 1 hour.
finally:
transport.close()
class EchoClientProtocol:
def __init__(self, message, on_con_lost):
self.message = message
self.on_con_lost = on_con_lost
self.transport = None
def connection_made(self, transport):
self.transport = transport
print('Send:', self.message)
self.transport.sendto(self.message.encode())
def datagram_received(self, data, addr):
print("Received:", data.decode())
print("Close the socket")
self.transport.close()
def error_received(self, exc):
print('Error received:', exc)
def connection_lost(self, exc):
print("Connection closed")
self.on_con_lost.set_result(True)
async def client_main():
# Get a reference to the event loop as we plan to use
# low-level APIs.
loop = asyncio.get_running_loop()
on_con_lost = loop.create_future()
message = "Hello World!"
transport, protocol = await loop.create_datagram_endpoint(
lambda: EchoClientProtocol(message, on_con_lost),
remote_addr=('127.0.0.1', 9999))
try:
await on_con_lost
finally:
transport.close()
if __name__ == '__main__':
if sys.argv[1] == '-s':
asyncio.run(server_main())
else:
asyncio.run(client_main())

2
axum/simulink_bridge/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
Cargo.lock
/target

View File

@ -0,0 +1,19 @@
[package]
name = "simulink_bridge"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = { version = "0.6.11", features = ["headers", "http1", "json", "macros", "matched-path", "multipart", "tokio", "tower-log", "ws", "form", "query"] }
chrono = "0.4.24"
serde = { version = "1.0.156", features = ["derive"] }
sqlx = { version = "0.6.3", features = ["runtime-tokio-native-tls", "mysql", "chrono"] }
tokio = { version = "1.26.0", features = ["full"] }
tower = { version = "0.4.13", features = ["full"] }
tower-http = { version = "0.4.0", features = ["add-extension", "trace"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
uuid = { version = "1.3.0", features = ["serde", "v4"] }

View File

@ -0,0 +1,16 @@
# Axum
Install via commands:
``` shell
cargo add axum -F "headers,http1,json,macros,matched-path,multipart,tokio,tower-log,ws,form,query"
cargo add sqlx -F "runtime-tokio-native-tls,mysql,chrono"
cargo add serde -F "derive"
cargo add tokio -F "full"
cargo add tower -F "full"
cargo add tower-http -F "add-extension,trace"
cargo add tracing
cargo add tracing-subscriber -F "env-filter"
cargo add uuid -F "serde,v4"
```

View File

@ -0,0 +1,206 @@
//! Provides a RESTful web server managing some Todos.
//!
//! API will be:
//!
//! - `GET /todos`: return a JSON list of Todos.
//! - `POST /todos`: create a new Todo.
//! - `PUT /todos/:id`: update a specific Todo.
//! - `DELETE /todos/:id`: delete a specific Todo.
//!
//! Run with
//!
//! ```not_rust
//! cargo run -p example-todos
//! ```
use axum::{
error_handling::HandleErrorLayer,
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
routing::{get, patch},
Json, Router,
};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
net::SocketAddr,
sync::{Arc, RwLock},
time::Duration,
};
use tower::{BoxError, ServiceBuilder};
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use uuid::Uuid;
use sqlx::mysql::{MySqlConnectOptions, MySqlSslMode, MySqlPool};
use chrono::NaiveDateTime;
#[derive(Debug, PartialEq, Eq, sqlx::FromRow)]
struct TechNews {
id: i32,
name: String,
datetime: NaiveDateTime,
data: String
}
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "example_todos=debug,tower_http=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let db = Db::default();
// Compose the routes
let app = Router::new()
.route("/todos", get(todos_index).post(todos_create))
.route("/todos/:id", patch(todos_update).delete(todos_delete))
// Add middleware to all routes
.layer(
ServiceBuilder::new()
.layer(HandleErrorLayer::new(|error: BoxError| async move {
if error.is::<tower::timeout::error::Elapsed>() {
Ok(StatusCode::REQUEST_TIMEOUT)
} else {
Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
))
}
}))
.timeout(Duration::from_secs(10))
.layer(TraceLayer::new_for_http())
.into_inner(),
)
.with_state(db);
// let database_url = "mysql://srcjvn0jcluz:pscale_pw_yLytUUayIAzj6oIOstDE5kl84QlMjpkxSLkwr51ogC8@i050z8tvw4s4.ap-northeast-2.psdb.cloud/feishu";
let mut options = MySqlConnectOptions::new()
.host("i050z8tvw4s4.ap-northeast-2.psdb.cloud")
// .port(3306)
.username("srcjvn0jcluz")
.password("pscale_pw_yLytUUayIAzj6oIOstDE5kl84QlMjpkxSLkwr51ogC8")
.database("feishu")
.ssl_mode(MySqlSslMode::Required);
// Use native-tls instead of rustls
options = options.ssl_mode(MySqlSslMode::Required).ssl_ca("/etc/ssl/certs/ca-certificates.crt");
let pool = MySqlPool::connect_with(options).await?;
// Use the pool to execute queries
let row = sqlx::query_as::<_, TechNews>("SELECT * FROM TechNews")
.fetch_one(&pool)
// .fetch_all(&pool)
.await?;
println!("row: {:?}", row);
// println!("id: {}, name: {}, datetime: {}, data: {}", row.get(0), row.get(1), row.get(2), row.get(3));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("Listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
Ok(())
}
// The query parameters for todos index
#[derive(Debug, Deserialize, Default)]
pub struct Pagination {
pub offset: Option<usize>,
pub limit: Option<usize>,
}
async fn todos_index(
pagination: Option<axum::extract::Query<Pagination>>,
State(db): State<Db>,
) -> impl IntoResponse {
let todos = db.read().unwrap();
let axum::extract::Query(pagination) = pagination.unwrap_or_default();
let todos = todos
.values()
.skip(pagination.offset.unwrap_or(0))
.take(pagination.limit.unwrap_or(usize::MAX))
.cloned()
.collect::<Vec<_>>();
Json(todos)
}
#[derive(Debug, Deserialize)]
struct CreateTodo {
text: String,
}
async fn todos_create(State(db): State<Db>, Json(input): Json<CreateTodo>) -> impl IntoResponse {
let todo = Todo {
id: Uuid::new_v4(),
text: input.text,
completed: false,
};
db.write().unwrap().insert(todo.id, todo.clone());
(StatusCode::CREATED, Json(todo))
}
#[derive(Debug, Deserialize)]
struct UpdateTodo {
text: Option<String>,
completed: Option<bool>,
}
async fn todos_update(
Path(id): Path<Uuid>,
State(db): State<Db>,
Json(input): Json<UpdateTodo>,
) -> axum::response::Result<impl IntoResponse, StatusCode> {
let mut todo = db
.read()
.unwrap()
.get(&id)
.cloned()
.ok_or(StatusCode::NOT_FOUND)?;
if let Some(text) = input.text {
todo.text = text;
}
if let Some(completed) = input.completed {
todo.completed = completed;
}
db.write().unwrap().insert(todo.id, todo.clone());
Ok(Json(todo))
}
async fn todos_delete(Path(id): Path<Uuid>, State(db): State<Db>) -> impl IntoResponse {
if db.write().unwrap().remove(&id).is_some() {
StatusCode::NO_CONTENT
} else {
StatusCode::NOT_FOUND
}
}
type Db = Arc<RwLock<HashMap<Uuid, Todo>>>;
#[derive(Debug, Serialize, Clone)]
struct Todo {
id: Uuid,
text: String,
completed: bool,
}

3
axum/simulink_bridge/test.sh Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
curl -X POST 'http://localhost:3000/todos' -H "Content-Type: application/json" -H "Accept: application/json" --data '{"text": "first"}' -vv

7
cdll-rs/.cargo/config Normal file
View File

@ -0,0 +1,7 @@
[build]
target = "x86_64-unknown-linux-gnu"
[target.x86_64-unknown-linux-gnu]
linker = "x86_64-nilrt-linux-gcc"
rustflags = [
"-C", "link-arg=--sysroot=C:/build/17.0/x64/sysroots/core2-64-nilrt-linux"]

3
cdll-rs/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.exe
Cargo.lock
/target

13
cdll-rs/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "cdll"
version = "0.1.0"
edition = "2021"
[dependencies]
libloading = "0.8.0"
[profile.release]
strip = true
opt-level = "z" # Optimize for size
lto = true
codegen-units = 1

18
cdll-rs/build.ps1 Normal file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env pwsh
#
# Build the Rust version cdll
# Update the PATH environment variable
$env:PATH = "C:\build\17.0\x64\sysroots\i686-nilrtsdk-mingw32\usr\bin\x86_64-nilrt-linux;" + [System.Enviro
nment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine) + ";" + [System.Environment]::
GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::User)
# cargo build --target x86_64-unknown-linux-gnu
cargo build
if ($?) {
scp $PSScriptRoot\target\x86_64-unknown-linux-gnu\Debug\cdll NI:~
ssh NI "chmod +x cdll"
} else {
Write-Host "Failed to build"
}

262
cdll-rs/src/main.rs Normal file
View File

@ -0,0 +1,262 @@
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
use std::{env, ffi::{c_char, c_void, CString}};
use libloading;
// Constants
const IntfBaudRate: u32 = 0x00100016;
// Start stop mode
const StartStopNormal: u32 = 0;
const StartStopSessionOnly: u32 = 1;
const StartStopInterfaceOnly: u32 = 2;
const StartStopSessionOnlyBlocking: u32 = 3;
// NI XNET types
type NIXNetEngineSessionRef = u32;
type NIXNetStatus = i32;
type NIXNetTimestamp100ns = u64;
type CreateSessionFunc = unsafe extern "C" fn(
dbname: *const c_char,
cluster_name: *const c_char,
list: *const c_char,
interface: *const c_char,
mode: u32,
session_ref: *mut NIXNetEngineSessionRef,
) -> NIXNetStatus;
type ClearFunc = unsafe extern "C" fn(session_ref: NIXNetEngineSessionRef) -> NIXNetStatus;
type StatusToStringFunc = unsafe extern "C" fn(status: NIXNetStatus, size: u32, msg: *mut c_char);
type SetPropertyFunc = unsafe extern "C" fn(
session: NIXNetEngineSessionRef,
property_id: u32,
property_size: u32,
property: *mut c_void,
) -> NIXNetStatus;
type StartFunc = unsafe extern "C" fn(session: NIXNetEngineSessionRef, scope: u32) -> NIXNetStatus;
type StopFunc = unsafe extern "C" fn(session: NIXNetEngineSessionRef, scope: u32) -> NIXNetStatus;
type ReadFrameFunc = unsafe extern "C" fn(
session: NIXNetEngineSessionRef,
buffer: *mut c_void,
size_of_buffer: u32,
timeout: f64,
number_of_bytes_returned: *mut u32,
) -> NIXNetStatus;
#[repr(C)]
pub struct FrameCAN {
timestamp: NIXNetTimestamp100ns,
identifier: u32,
kind: u8,
flags: u8,
info: u8,
payload_length: u8,
payload: [u8; 64],
}
#[repr(C)]
struct EngineInterface {
_lib: libloading::Library,
_create_session: Result<CreateSessionFunc, libloading::Error>,
_clear: Result<ClearFunc, libloading::Error>,
_status_to_string: Result<StatusToStringFunc, libloading::Error>,
_start: Result<StartFunc, libloading::Error>,
_stop: Result<StopFunc, libloading::Error>,
_set_property: Result<SetPropertyFunc, libloading::Error>,
_read_frame: Result<ReadFrameFunc, libloading::Error>,
_session: NIXNetEngineSessionRef,
}
impl EngineInterface {
pub fn new<P>(lib_path: P) -> Result<Self, libloading::Error>
where
P: AsRef<std::ffi::OsStr>,
{
unsafe {
let library = libloading::Library::new(lib_path)?;
Self::from_library(library)
}
}
fn from_library<L>(library: L) -> Result<Self, libloading::Error>
where
L: Into<libloading::Library>,
{
unsafe {
let lib = library.into();
let create_session = lib.get("nxCreateSession".as_bytes()).map(|sym| *sym);
let clear = lib.get("nxClear".as_bytes()).map(|sym| *sym);
let status_to_string = lib.get("nxStatusToString".as_bytes()).map(|sym| *sym);
let start = lib.get("nxStart".as_bytes()).map(|sym| *sym);
let stop = lib.get("nxStop".as_bytes()).map(|sym| *sym);
let set_property = lib.get("nxSetProperty".as_bytes()).map(|sym| *sym);
let read_frame = lib.get("nxReadFrame".as_bytes()).map(|sym| *sym);
Ok(EngineInterface {
_lib: lib,
_create_session: create_session,
_clear: clear,
_status_to_string: status_to_string,
_start: start,
_stop: stop,
_set_property: set_property,
_read_frame: read_frame,
_session: 0,
})
}
}
pub fn create_session(&mut self, db: &str, dev: &str) -> NIXNetStatus {
let cdb = CString::new(db).expect("Failed to get db name");
let cdev = CString::new(dev).expect("Failed to get dev name");
unsafe {
(self
._create_session
.as_ref()
.expect("create_session got error!"))(
cdb.as_ptr() as *const c_char,
std::ptr::null() as *const c_char,
std::ptr::null() as *const c_char,
cdev.as_ptr() as *const c_char,
6,
&mut self._session as *mut u32,
)
}
}
pub fn clear(&mut self) -> NIXNetStatus {
unsafe { (self._clear.as_ref().expect("clear got error!"))(self._session) }
}
pub fn status_to_string(&mut self, status: NIXNetStatus) -> String {
let mut output_buffer: [c_char; 4096] = [0; 4096];
let output_buffer_ptr = output_buffer.as_mut_ptr();
let bytes = unsafe {
(self
._status_to_string
.as_ref()
.expect("status_to_string got error!"))(status, 4096, output_buffer_ptr);
let buf = std::slice::from_raw_parts(output_buffer_ptr, 4096);
std::mem::transmute::<&[i8], &[u8]>(buf)
};
String::from(std::str::from_utf8(bytes).unwrap())
}
pub fn start(&mut self, scope: u32) -> NIXNetStatus {
unsafe { (self._start.as_ref().expect("Failed to invoke nxStart!"))(self._session, scope) }
}
pub fn stop(&mut self, scope: u32) -> NIXNetStatus {
unsafe { (self._stop.as_ref().expect("Failed to invoke nxStop!"))(self._session, scope) }
}
pub fn set_property(&mut self, property_id: u32, property: u32) -> NIXNetStatus {
let bytes = property.to_le_bytes();
let ptr = bytes.as_ptr();
unsafe {
let void_ptr = std::mem::transmute::<*const u8, *mut c_void>(ptr);
(self
._set_property
.as_ref()
.expect("Failed to invoke nxSetProperty"))(
self._session, property_id, 4, void_ptr
)
}
}
pub fn read_can_frame(&mut self) -> Option<FrameCAN> {
let frame = FrameCAN {
timestamp: 0,
identifier: 0,
kind: 0,
flags: 0,
info: 0,
payload_length: 0,
payload: [0; 64],
};
unsafe {
let ptr = &frame as *const FrameCAN as *const u8;
let struct_bytes = std::slice::from_raw_parts(ptr, std::mem::size_of::<FrameCAN>());
let mut read_size: u32 = 0;
(self
._read_frame
.as_ref()
.expect("Failed to invoke nxReadFrame!"))(
self._session,
struct_bytes.as_ptr() as *mut c_void,
std::mem::size_of::<FrameCAN>() as u32,
0.0,
&mut read_size as *mut u32,
);
match read_size {
0 => None,
_ => Some(frame)
}
}
}
}
fn main() -> Result<(), libloading::Error> {
let args: Vec<String> = env::args().collect();
let db = args[1].as_str();
let dev = args[2].as_str();
let mut eng = EngineInterface::new("libnixnet.so")?;
let mut status = eng.create_session(&db, &dev);
println!("Status: 0x{:X}", status);
if status != 0 {
println!(
"[E] Failed to create session on DB<{}>, DEV<{}>",
&args[1], &args[2]
);
println!("Error message: {}", eng.status_to_string(status));
}
status = eng.set_property(IntfBaudRate, 500000);
if status != 0 {
println!(
"[E] Failed to set property baudrate on DB<{}>, DEV<{}>",
&args[1], &args[2]
);
println!("Error message: {}", eng.status_to_string(status));
}
// Read frame buffer
eng.start(StartStopNormal);
let mut i: i32 = 0;
while i < 10
{
let frame = eng.read_can_frame();
match frame {
Some(f) => {
println!("Frame Type: {}", f.kind);
i = i + 1;
},
_ => continue
}
}
eng.stop(StartStopNormal);
eng.clear();
Ok(())
}

31
coordinate/wgs842gauss.py Normal file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
# -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8
import sys
import pyproj
import math
BASE_X_OFFSET = 20500000
GAUSSIAN_INIT_STR = "+proj=tmerc +ellps=WGS84 +lon_0={:.2f}e +x_0=20500000 +y_0=0 +units=m +k=1.0"
def wgs84_gaussian(lon, lat, centrial_meridian) -> tuple:
gaussian = pyproj.CRS.from_proj4(GAUSSIAN_INIT_STR.format(centrial_meridian))
wgs84 = pyproj.CRS("EPSG:4326")
transformer = pyproj.Transformer.from_crs(wgs84, gaussian, always_xy=True)
gx, gy = transformer.transform(lon, lat)
return (gx, gy)
def gaussian_wgs84(gx, gy, centrial_meridian) -> tuple:
gaussian = pyproj.CRS.from_proj4(GAUSSIAN_INIT_STR.format(centrial_meridian))
wgs84 = pyproj.CRS("EPSG:4326")
transformer = pyproj.Transformer.from_crs(gaussian, wgs84, always_xy=True)
lon, lat = transformer.transform(gx, gy)
return (lon, lat)
if __name__ == "__main__":
gx, gy = wgs84_gaussian(121.23452642, 40.1232463, 121.23)
print(f"WGS84 -> Gaussian: {gx}, {gy}")
lon, lat = gaussian_wgs84(gx, gy, 121.23)
print(f"Gaussian -> WGS84: {lon}, {lat}")

196
dbcc/.gitignore vendored Normal file
View File

@ -0,0 +1,196 @@
# AdMake
.build/
bin/
lib/
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# ---> C++
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# ---> CMake
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
.vscode/
.#*
.DS_Store
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

237
dbcc/CMakeLists.txt Normal file
View File

@ -0,0 +1,237 @@
cmake_minimum_required (VERSION 3.15)
include (declare)
enable_msvc_mt_md ()
project (dbcc)
# set (ADMAKE_ENABLE_EXCEPTION ON)
# set (ADMAKE_DISABLE_ADDRESS_SANITIZER ON)
set (ADMAKE_ENABLE_FIND_HOOK ON)
set (ADMAKE_IGNORE_VCPKG ON)
# set (ADMAKE_DISABLE_DEFAULT ON)
include (admake)
set (TARGET_NAME ${CMAKE_PROJECT_NAME})
include_directories (
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/source")
if (DEFINED ADMAKE_BUILD_TEST)
file (GLOB_RECURSE TEST_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/test/*.cpp")
add_executable (${TARGET_NAME}_test ${TEST_SRC})
add_dependencies (${TARGET_NAME}_test ${TARGET_NAME})
target_link_libraries (${TARGET_NAME}_test ${TARGET_NAME})
else ()
file (GLOB_RECURSE SRC
"${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
extract_version (
FILE ${CMAKE_CURRENT_SOURCE_DIR}/source/version.cpp
PREFIX "DBCC"
MAJOR OUTPUT_MAJOR
MINOR OUTPUT_MINOR
PATCH OUTPUT_PATCH)
set (DBCC_VERSION "${OUTPUT_MAJOR}.${OUTPUT_MINOR}.${OUTPUT_PATCH}")
add_library (${TARGET_NAME}_OBJ OBJECT ${SRC})
target_link_libraries (${TARGET_NAME}_OBJ
INTERFACE admake::disable_warnings
PRIVATE admake::disable_warnings)
target_link_libraries (${TARGET_NAME}_OBJ
INTERFACE admake::enable_pic
PUBLIC admake::enable_pic)
target_set_mt (${TARGET_NAME}_OBJ)
add_library (${TARGET_NAME} STATIC $<TARGET_OBJECTS:${TARGET_NAME}_OBJ>)
add_library (${TARGET_NAME}_SO SHARED $<TARGET_OBJECTS:${TARGET_NAME}_OBJ>)
set_target_properties (${TARGET_NAME}_SO PROPERTIES
OUTPUT_NAME "dbcc")
#-----------------------#
# Build Python3 binding #
#-----------------------#
find_package (PythonInterp 3)
find_package (PythonLibs 3)
# find_package (Python3)
if (PYTHONLIBS_FOUND)
# if (PYTHON3_FOUND)
# NOTE:
# Because of the stupid design of Python(in pyconfig.h, the author force link python_d for debug version), so when build debug version in AnaConda environment, maybe you can not find the debug version library, so you can only build the release version.
# Build python binding
set (PY3DIR "py3")
set (PY_TARGET_NAME py_${CMAKE_PROJECT_NAME})
file (GLOB_RECURSE PYSRC
"${CMAKE_CURRENT_SOURCE_DIR}/binding/py/*.cpp")
find_package (pybind11 CONFIG REQUIRED)
pybind11_add_module (${PY_TARGET_NAME} ${PYSRC})
target_include_directories (${PY_TARGET_NAME} PRIVATE
${pybind11_INCLUDE_DIR}
${PYTHON_INCLUDE_DIRS})
target_link_libraries (${PY_TARGET_NAME} PRIVATE $<TARGET_OBJECTS:${TARGET_NAME}_OBJ> admake::disable_warnings)
target_set_mt (${PY_TARGET_NAME})
add_dependencies (${PY_TARGET_NAME} ${TARGET_NAME}_OBJ)
if (WIN32)
set (MY_SUFFIX ".pyd")
elseif (APPLE)
set (MY_SUFFIX ".dylib")
else ()
set (MY_SUFFIX ".so")
endif ()
set_target_properties (${PY_TARGET_NAME} PROPERTIES
LIBRARY_OUTPUT_NAME "_${CMAKE_PROJECT_NAME}"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${PY3DIR}/dbcc"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${PY3DIR}/dbcc"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${PY3DIR}/dbcc"
PREFIX ""
SUFFIX "${MY_SUFFIX}"
)
set (INIT_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/binding/py/__init__.py")
set (INIT_OUTPUT "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${PY3DIR}/dbcc/__init__.py")
get_filename_component (DESTINATION ${INIT_OUTPUT} PATH)
add_custom_command (TARGET ${PY_TARGET_NAME} POST_BUILD
DEPENDS ${INIT_INPUT}
COMMAND ${CMAKE_COMMAND} -E make_directory ${DESTINATION}
COMMAND ${CMAKE_COMMAND} -E copy ${INIT_INPUT} ${INIT_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${PY_TARGET_NAME}> "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${PY3DIR}/dbcc")
# Build python3 package
# Generate setup.py file
configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/binding/py/setup.py.in"
"${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${PY3DIR}/setup.py" @ONLY)
set (SETUP_PY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${PY3DIR}/setup.py")
if (WIN32)
string (REPLACE "/" "\\" DEST_DIR "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}")
add_custom_command (TARGET ${PY_TARGET_NAME} POST_BUILD
WORKING_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${PY3DIR}
COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} bdist_wheel
COMMAND xcopy "dist" "${DEST_DIR}" /c /g /d /e /r /h /y
COMMENT "Build wheel package for DBCC Python3, ${PYPKG_SUFFIX}"
DEPENDS ${SETUP_PY} ${INIT_OUTPUT})
else ()
add_custom_command (TARGET ${PY_TARGET_NAME} POST_BUILD
WORKING_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${PY3DIR}
COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} -q bdist_wheel
COMMAND cp "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${PY3DIR}/dist/*.whl" ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
COMMENT "Build wheel package for DBCC Python3, ${PYPKG_SUFFIX}"
DEPENDS ${SETUP_PY} ${INIT_OUTPUT})
endif ()
add_custom_command (TARGET ${PY_TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${PY3DIR}"
DEPENDS ${INIT_OUTPUT})
#-------------------#
# Build LUA binding #
#-------------------#
# append_3rd_modules (lua)
# file (GLOB_RECURSE LUA_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/binding/lua/*.cpp")
# add_library (lua_dbcc SHARED ${LUA_SRCS})
# target_link_libraries (lua_dbcc ${LUA_LIB})
# set_target_properties (lua_dbcc PROPERTIES
# OUTPUT_NAME "dbcc")
#--------------------#
# Build RUST binding #
#--------------------#
# LTO
# option (ENABLE_LTO "Enable cross language linking time optimization" ON)
# if (ENABLE_LTO)
# include (CheckIPOSupported)
# check_ipo_supported (RESULT supported OUTPUT error)
# if (supported)
# message (STATUS "IPO / LTO enabled")
# set (CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
# add_link_options (-fuse-ld=lld)
# else ()
# message (STATUS "IPO / LTO not supported: <${error}>")
# endif ()
# endif ()
# set (RUST_TARGE_NAME "rdbcc")
# if (NOT DEFINED ENV{CARGO_EXE})
# find_program (CARGO_EXE cargo)
# endif ()
# if (NOT DEFINED ENV{RUSTC_EXE})
# find_program (RUSTC_EXE rustc)
# endif ()
# if ("${CARGO_EXE}" STREQUAL "CARGO_EXE-NOTFOUND")
# message_color ("Red" "-- Command cargo is required, but do not exist in the PATH!")
# message (FATAL_ERROR "Command cargo is required, but do not exist in the PATH!")
# endif ()
# if ("${RUSTC_EXE}" STREQUAL "RUSTC_EXE-NOTFOUND")
# message_color ("Red" "-- Command rustc is required, but do not exist in the PATH!")
# message (FATAL_ERROR "Command rustc is required, but do not exist in the PATH!")
# endif ()
# if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
# set (CARGO_CMD ${CARGO_EXE} build --release --verbose)
# set (TARGET_DIR "release")
# else ()
# set (CARGO_CMD ${CARGO_EXE} build --verbose)
# set (TARGET_DIR "debug")
# endif ()
# if (ENABLE_LTO)
# set (RUST_FLAGS "-Clinker-plugin-lto" "-Clinker=clang-13" "-Clink-arg=-fuse-ld=lld-13")
# endif ()
# if (WIN32)
# # set (RS_SO "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/${RUST_TARGE_NAME}.dll")
# set (RS_LIB "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/${RUST_TARGE_NAME}.lib")
# elseif (APPLE)
# # set (RS_SO "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/lib${RUST_TARGE_NAME}.dylib")
# set (RS_LIB "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/lib${RUST_TARGE_NAME}.a")
# else ()
# # set (RS_SO "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/lib${RUST_TARGE_NAME}.so")
# set (RS_LIB "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/lib${RUST_TARGE_NAME}.a")
# endif ()
# add_custom_target (rs ALL
# COMMENT "Compiling RUST binding module"
# COMMAND CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} RUSTFLAGS="${RUST_FLAGS}" ${CARGO_CMD}
# COMMAND cmake -E copy_if_different ${RS_LIB} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/binding/rust)
# set_target_properties (rs PROPERTIES LOCATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
# set(RUST_PART_CXX "${CMAKE_CURRENT_BINARY_DIR}/rust_part.cpp")
# add_library(rust_part STATIC ${RUST_PART_CXX})
# add_custom_command(
# OUTPUT ${RUST_PART_CXX}
# COMMAND CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} RUSTFLAGS="${RUST_FLAGS}" ${CARGO_CMD}
# COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/rdbcc/src/lib.rs.cc ${RUST_PART_CXX}
# COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/rdbcc/src/lib.rs.h ${CMAKE_CURRENT_BINARY_DIR}/rust_part.h
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
# )
# target_link_libraries (rust_part pthread dl ${RUST_PART_LIB})
# add_test (NAME rs_test
# COMMAND ${CARGO_EXE} test
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/binding/rust)
endif ()
endif ()

7
dbcc/LICENSE Normal file
View File

@ -0,0 +1,7 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

5
dbcc/README.md Normal file
View File

@ -0,0 +1,5 @@
# DBC Compiler and Parser
A library to help tools parse the DBC file and convert them to other formats.
:note: C++ 11 is required.

View File

@ -0,0 +1,20 @@
namespace Dbcc {
public enum ByteOrder : int
{
Motorola, ///< 0 big-endbian
Intel ///< 1 little-endbian
}
public enum Sign : int
{
Unsigned,
Signed
}
public enum Multiplexor : int
{
None,
Multiplexed,
Multiplexor
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Runtime.InteropServices;
namespace Dbcc
{
/// <summary>
/// Imported symbols for interop with dbcc.dll/so.
/// </summary>
internal class NativeInterop
{
// This shouldn't be needed, even on Windows
// /// <summary>
// /// Taken from: http://stackoverflow.com/questions/10852634/using-a-32bit-or-64bit-dll-in-c-sharp-dllimport
// /// </summary>
// static NativeInterop()
// {
// var myPath = new Uri(typeof(NativeInterop).Assembly.CodeBase).LocalPath;
// var myFolder = Path.GetDirectoryName(myPath);
// var is64 = IntPtr.Size == 8;
// var subfolder = is64 ? "\\win64\\" : "\\win32\\";
// string dllPosition = myFolder + subfolder + "keystone.dll";
// // If this file exist, load it.
// // Otherwise let the marshaller load the appropriate file.
// if (File.Exists(dllPosition))
// LoadLibrary(dllPosition);
// }
// [DllImport("dbcc.dll")]
// private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("dbcc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "version" )]
internal static extern uint Version(ref uint major, ref uint minor);
[DllImport("dbcc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SignalProcessor_new")]
internal static extern uint Create(ref IntPtr h, [MarshalAs(UnmanagedType.LPStr)] string filePath);
[DllImport("dbcc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SignalProcessor_delete")]
internal static extern void Dispose(IntPtr h);
[DllImport("dbcc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SignalProcessor_encode")]
internal static extern int Encode(IntPtr h,
double pvs,
[MarshalAs(UnmanagedType.LPArray)] ref byte[] data, uint length);
[DllImport("dbcc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SignalProcessor_decode")]
bool Decode(IntPtr h, uint msgId, [MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
namespace Dbcc
{
public sealed class SignalProcessor : IDisposable
{
private IntPtr _handle = IntPtr.Zero;
protected virtual void Dispose() {
IntPtr currentEngine = Interlocked.Exchange(ref _handle, IntPtr.Zero);
if (currentEngine != IntPtr.Zero) {
NativeInterop.Dispose(currentEngine);
}
}
}
}

129
dbcc/binding/js/.gitignore vendored Normal file
View File

@ -0,0 +1,129 @@
# test data
test-data/
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

View File

@ -0,0 +1,3 @@
# JavaScript Binding of DBCC
> NOTE: In fact, it is not a script binding implementation, I just re-implement the parser with JavaScript.

555
dbcc/binding/js/dbcc.js Normal file
View File

@ -0,0 +1,555 @@
/**
* @license
* Copyright 2021- anjingyu <anjingyu@navinfo.com>
* SPDX-License-Identifier: WTFPL 2
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.DBCC = {}));
})(this, (function (exports) {
'use strict';
const VERSION = '1.0.0';
const MSG_REGEXP = /BO_\s+([0-9]+)\s+([^\s]+):\s*([0-9]{1,2})\s+(.*)?/;
const SIG_REGEXP = /\s+SG_\s+([^\s]+)\s*:\s*([0-9]{1,2})\|([0-9]{1,2})@([0-1])([+-])\s+\((.*)?,(.*)?\)\s+\[(.*)?\|(.*)?\]\s+"(.*)?"\s{1,2}(.*)?/;
const ByteOrder = {
Motorola: 0, ///< 0 big-endbian
Intel: 1 ///< 1 little-endbian
};
const Sign = {
Unsigned: 0,
Signed: 1
};
const ShiftDirection = {
Left: 0,
Right: 1
};
Object.freeze(ByteOrder);
Object.freeze(Sign);
Object.freeze(ShiftDirection);
class Message {
#name;
#id;
#dlc;
#from;
#to;
#signals;
#indexes;
constructor(name, id, dlc, from) {
this.#name = name;
this.#id = id;
this.#dlc = dlc
this.#from = from
this.#to = [];
this.#signals = [];
this.#indexes = {};
}
get name() {
return this.#name;
}
get id() {
return this.#id;
}
get dlc() {
return this.#dlc;
}
get from() {
return this.#from;
}
get to() {
if (!this.#to.length) {
let toMap = new Map();
for (let sig in this.#signals) {
for (let t in sig.to) {
toMap.set(t, '');
}
}
this.#to = Array.from(toMap.keys());
}
return this.#to;
}
[Symbol.iterator]() {
let index = -1;
let signals = this.#signals;
return {
next: () => ({value: signals[++index], done: index == signals.length})
};
}
append(signal) {
this.#signals.push(signal);
this.#indexes[signal.name] = this.#signals.length - 1;
}
static fromLineString(lineString) {
let items = lineString.match(MSG_REGEXP);
if (items) {
return new Message(items[2], parseInt(items[1]), parseInt(items[3]), items[4]);
} else {
return null;
}
}
}
class CompiledSignalParameter {
#index;
#shift; ///< TODO(donkey): Optimization, use the highest bit to indicate the shift direction.
#mask;
#direction; ///< 0: left shift, 1: right shift
constructor(index, shift, mask, direction) {
this.#index = index;
this.#shift = shift;
this.#mask = mask;
this.#direction = direction;
}
get index() {
return this.#index;
}
get mask() {
return this.#mask;
}
get shift() {
return this.#shift;
}
get direction() {
return this.#direction;
}
}
class Signal {
#name;
#byteOrder;
#startBit;
#length;
#sign;
#minimum;
#maximum;
#factor;
#offset;
#unit;
#to;
#isFloat;
#compiled;
#signedLengthMask;
constructor(name, startBit, length, byteOrder, sign, factor, offset, minimum, maximum, unit, to) {
this.#name = name;
this.#startBit = startBit;
this.#length = length;
this.#byteOrder = byteOrder;
this.#sign = sign;
this.#factor = factor;
this.#offset = offset;
this.#minimum = minimum;
this.#maximum = maximum;
this.#unit = unit;
this.#to = to;
let f = (x) => (~~x == x);
if (f(minimum) && f(maximum) && f(factor) && f(offset)) {
this.#isFloat = false;
} else {
this.#isFloat = true;
}
this.#compiled = this.#segments(true);
this.#signedLengthMask = 0;
if (this.#sign == Sign.Signed)
{
let mask = ((1 << (this.#typeLength() - this.#length)) - 1);
if (mask != 0) {
mask <<= this.#length;
this.#signedLengthMask = mask;
}
}
}
#typeLength() {
let len = this.#length;
if (len <= 8) {
return 8;
} else if (len <= 16) {
return 16;
} else if (len <= 32) {
return 32;
}
return 64;
}
#segments(invertShift) {
invertShift = invertShift || false;
let segs = [];
let index = parseInt(this.#startBit / 8);
let pos = this.#startBit % 8;
let left = this.#length;
let length = 0;
let tmpShift = 0, tmpMask = 0, tmpDirection = 0, tmpIndex = 0;
while (left > 0) {
if (this.#byteOrder == ByteOrder.Motorola) {
if (left >= (pos + 1)) {
length = (pos + 1);
pos = 7;
tmpShift = -(left - length);
tmpMask = ((1 << length) - 1);
} else {
length = left;
tmpShift = (pos - length + 1);
tmpMask = ((1 << length) - 1);
tmpMask <<= (pos - length + 1);
}
} else {
tmpShift = (left - this.#length) + pos;
if (left >= (8 - pos)) {
length = (8 - pos);
tmpMask = ((1 << length) - 1);
tmpMask <<= pos;
pos = 0;
}
else {
length = left;
tmpMask = ((1 << length) - 1);
tmpMask <<= pos;
}
}
if (invertShift) {
if (tmpShift < 0) {
tmpShift = -tmpShift;
tmpDirection = ShiftDirection.Left;
}
else {
tmpDirection = ShiftDirection.Right;
}
} else {
if (tmpShift < 0) {
tmpShift = -tmpShift;
tmpDirection = ShiftDirection.Right;
}
else {
tmpDirection = ShiftDirection.Left;
}
}
tmpIndex = index;
segs.push(new CompiledSignalParameter(tmpIndex, tmpShift, tmpMask, tmpDirection));
left -= length;
index += 1;
}
return segs;
}
#packLeftShift(value, shift, mask) {
return ((value << shift) & mask) & 0xff;
}
#packRightShift(value, shift, mask) {
return ((value >> shift) & mask) & 0xff;
}
#unpackLeftShift(value, shift, mask) {
return ((value & mask) << shift);
}
#unpackRightShift(value, shift, mask) {
return ((value & mask) >> shift);
}
// Return ArrayBuffer<Uint8>
encode(value) {
let temp = 0;
let data = new Uint8Array(8);
if (!this.#isFloat) {
temp = ((parseInt(value) - this.#offset) / this.#factor);
} else {
temp = ((value - this.#offset) / this.#factor);
}
for (let i = 0; i < this.#compiled.length; ++i) {
let p = this.#compiled[i];
// NOTE(anjingyu): Here we encode the data, so the direction
// is reversed.
if (p.direction == ShiftDirection.Left) {
data[p.index] |= this.#packRightShift(temp, p.shift, p.mask);
} else {
data[p.index] |= this.#packLeftShift(temp, p.shift, p.mask);
}
}
return data;
}
// Return a double value
decode(data) {
let result = 0;
for (let i = 0; i < this.#compiled.length; ++i) {
let p = this.#compiled[i];
if (p.direction == ShiftDirection.Left) {
result |= this.#unpackLeftShift(data[p.index], p.shift, p.mask);
} else {
result |= this.#unpackRightShift(data[p.index], p.shift, p.mask);
}
}
if (this.#signedLengthMask != 0) {
if ((result & (1 << (this.#length - 1))) != 0) {
result |= this.#signedLengthMask;
}
}
return (result * this.#factor + this.#offset);
}
get name() {
return this.#name;
}
get startBit() {
return this.#startBit;
}
get length() {
return this.#length;
}
get byteOrder() {
return this.#byteOrder;
}
get sign() {
return this.#sign;
}
get minimum() {
return this.#minimum;
}
get maximum() {
return this.#maximum;
}
get factor() {
return this.#factor;
}
get offset() {
return this.#offset;
}
get unit() {
return this.#unit;
}
get to() {
return this.#to;
}
get isFloat() {
return this.#isFloat;
}
static fromLineString(lineString) {
let items = lineString.match(SIG_REGEXP);
if (items) {
return new Signal(items[1], ///< name
parseInt(items[2]), ///< startBit
parseInt(items[3]), ///< length
parseInt(items[4]), ///< byteOrder
items[5] === '+' ? Sign.Unsigned : Sign.Signed, ///< sign
parseFloat(items[6]), ///< factor
parseFloat(items[7]), ///< offset
parseFloat(items[8]), ///< minimum
parseFloat(items[9]), ///< maximum
items[10], ///< unit string
items[11].split(",")); ///< to list
} else {
return null;
}
}
}
class DbcParser {
#messages;
#indexes;
constructor() {
this.#messages = [];
this.#indexes = {};
}
[Symbol.iterator]() {
let index = -1;
let msgs = this.#messages;
return {
next: () => ({value: msgs[++index], done: index == msgs.length})
};
}
parse(contentString) {
// Traverse the contentString line by line,
// BO_ and SG_ are the interested lines(Message and Signal).
let lines = contentString.split(/\n/);
for (let i = 0; i < lines.length; ++i) {
let line = lines[i];
let signature = line.trim().split(" ")[0];
if (signature != "BO_") {
continue;
} else {
// Parse the Message Object and corresponding Signal Objects
let message = Message.fromLineString(line.trim());
this.#messages.push(message);
this.#indexes[message.name] = {};
for (let j = i + 1; j < lines.length; j = i + 1) {
let subline = lines[j];
signature = subline.trim().split(" ")[0];
if (signature != "SG_") {
--i;
break;
} else {
let sig = Signal.fromLineString(subline.trimEnd());
message.append(sig);
++i;
}
}
}
}
}
}
class SignalProcessor {
#dbcParser;
#msgs;
#handlers;
constructor(contentString) {
this.#dbcParser = new DbcParser();
this.#msgs = new Map();
this.#dbcParser.parse(contentString);
for (let msg of this.#dbcParser) {
if (msg.dlc == 0) {
continue;
}
let msgEntity = [msg, {}];
for (let sig of msg) {
msgEntity[1][sig.name] = sig;
}
this.#msgs.set(msg.id, msgEntity);
}
this.#handlers = new Map();
}
// Register the delegate to encode/decode the message
// the delegate should contains two methods, onEncode/onDecode
registerDelegate(msgId, sigName, delegate) {
if (this.#msgs.has(msgId) && sigName in this.#msgs.get(msgId)[1]) {
if (!this.#handlers.has(msgId)) {
this.#handlers.set(msgId, {});
}
this.#handlers.get(msgId)[sigName] = delegate;
return true;
}
return false;
}
unregisterDelegate(msgId, sigName) {
if (this.#handlers.has(msgId) && sigName in this.#handlers.get(msgId)) {
delete this.#handlers.get(msgId)[sigName];
if (Object.keys(this.#handlers.get(msgId)).length == 0) {
this.#handlers.delete(msgId);
}
}
}
decodeMessage(msgId, data) {
if (this.#handlers.has(msgId)) {
let sigs = this.#handlers.get(msgId);
for (let sigName in sigs) {
let sig = this.#msgs.get(msgId)[1][sigName];
let r = sig.decode(data);
sigs[sigName].onDecoded(msgId, sigName, r);
}
}
}
encodeMessage(msgId, values) {
if (this.#handlers.has(msgId)) {
let sigs = this.#handlers.get(msgId);
for (let sigName in values) {
let data = undefined;
if (sigName in sigs) {
let sig = this.#msgs.get(msgId)[1][sigName];
data = sig.encode(values[sigName]);
} else {
console.warn(`Did not register handler for signal (#{msgId}).{sigName}`)
}
sigs[sigName].onEncoded(msgId, data);
}
}
}
}
exports.VERSION = VERSION;
exports.Message = Message;
exports.Signal = Signal;
exports.DbcParser = DbcParser;
exports.SignalProcessor = SignalProcessor;
exports.ByteOrder = ByteOrder;
exports.Sign = Sign;
Object.defineProperty(exports, '__esModule', { value: true });
}));

View File

@ -0,0 +1,21 @@
{
"name": "dbcc",
"version": "1.0.0",
"description": "Parse the DBC file and parse the CAN messages in real-time.",
"main": "dbcc.js",
"files": [
"dbcc.js"
],
"scripts": {
"test": "node test.js"
},
"repository": {
"type": "git",
"url": "git@39.105.213.233:donkey/dbcc.git"
},
"keywords": [
"DBCC"
],
"author": "donkey <anjingyu_ws@foxmail.com>",
"license": "WTFPL 2"
}

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test DBCC API</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
margin: 0;
overflow: hidden;
}
table {
border-spacing: 0.5em 0;
}
.popup_info {
text-align: left;
font-family: monospace;
display: block;
color: whitesmoke;
}
.bottom_info {
position: absolute;
top: 1em;
left: 1em;
}
#file-dnd {
border: 5px dashed red;
width: 440px;
height: 272px;
}
</style>
</head>
<body>
<input id="file-selector" type="file" />
<div id="file-dnd"></div>
<ul id="output"></ul>
<script src="../dbcc.js"></script>
<script src="./test.js"></script>
</body>
</html>

View File

@ -0,0 +1,86 @@
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
} else {
alert('The File APIs are not fully supported in this browser.');
}
let fr = new FileReader();
// Please load the file: test-data/dbc.dbc
fr.onload = function() {
// Create parser instance
let parser = new DBCC.DbcParser();
parser.parse(fr.result);
// Dump the DBC file data
for (let msg of parser) {
console.log(msg);
for (let sig of msg) {
console.log(sig);
}
}
// Encode/decode test
let msgId = 578;
let sp = new DBCC.SignalProcessor(fr.result);
// Register delegate
let myhandler = {
onEncoded: function (msgId, db) {
console.log("msg:", msgId, ":", db);
},
onDecoded: function (msgId, signalName, value) {
console.log("msg:", msgId, ",", signalName, ":", value);
}
};
sp.registerDelegate(msgId, "ACCAccReqValHSC2", myhandler);
data = Uint8Array.from([0x01, 0xA8, 0, 0, 0, 0, 0, 0]);
sp.decodeMessage(msgId, data);
sp.encodeMessage(msgId, {"ACCAccReqValHSC2": -5.1});
}
const fileSelector = document.getElementById('file-selector');
const fileDnd = document.getElementById('file-dnd');
const output = document.getElementById('output');
// File selector demo
fileSelector.addEventListener('change', (event) => {
const files = event.target.files;
for (let i = 0; i < files.length; i++) {
const li = document.createElement('li');
const file = files[i];
const name = file.name ? file.name : 'NOT SUPPORTED';
const type = file.type ? file.type : 'NOT SUPPORTED';
const size = file.size ? file.size : 'NOT SUPPORTED';
li.textContent = `name: ${name}, type: ${type}, size: ${size}`;
output.appendChild(li);
fr.readAsText(file);
}
});
// DnD demo
fileDnd.addEventListener('dragover', event => {
event.stopPropagation();
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
});
fileDnd.addEventListener('drop', event => {
output.innerHTML = '';
event.stopPropagation();
event.preventDefault();
const files = event.dataTransfer.files;
for (let i = 0; i < files.length; i++) {
const li = document.createElement('li');
const file = files[i];
const name = file.name ? file.name : 'NOT SUPPORTED';
const type = file.type ? file.type : 'NOT SUPPORTED';
const size = file.size ? file.size : 'NOT SUPPORTED';
li.textContent = `name: ${name}, type: ${type}, size: ${size}`;
output.appendChild(li);
fr.readAsText(file);
}
});

104
dbcc/binding/lua/dbcc.lua Normal file
View File

@ -0,0 +1,104 @@
--[[
Thin wrapper of dbcc module for lua
Author: donkey <anjingyu_ws@foxmail.com>
--]]
local dbcc = {}
dbcc.ByteOrder = {
Motorola = 0, -- 0 big-endbian
Intel = 1 -- 1 little-endbian
}
dbcc.Sign = {
Unsigned = 0,
Signed = 1
}
dbcc.Multiplexor = {
None = 0,
Multiplexed = 1,
Multiplexor = 2
}
dbcc.dbcc = require("dbcc")
local SignalDelegate = {}
function SignalDelegate:new()
o = {}
setmetatable(o, self)
self.__index = self
return o
end
function SignalDelegate:on_encoded(msg_id, db):
end
function SignalDelegate:on_decoded(msg_id, signal_name, value):
end
dbcc.SignalDelegate = SignalDelegate
local SignalProcessor = {}
function SignalProcessor:new(dbc_file_path)
o = {dp = dbcc.dbcc.DbcParser:new(dbc_file_path), msgs = {}, handlers = {}}
setmetatable(o, self)
self.__index = self
-- Load messages from DBC file
for msg in self.dp do
if msg.dlc == 0 then
continue
end
end
return o
end
for msg in self.__dp:
if msg.dlc == 0:
continue
msg_entity = (msg, {})
for sig in msg:
msg_entity[1][sig.name] = sig
self.__msgs[msg.id] = msg_entity
def register_delegate(self, msg_id: int, sig_name: str, delegate: SignalDelegate) -> bool:
if msg_id in self.__msgs and sig_name in self.__msgs[msg_id][1]:
if msg_id not in self.__handlers:
self.__handlers[msg_id] = {}
self.__handlers[msg_id][sig_name] = delegate
return True
return False
def unregister_delegate(self, msg_id: int, sig_name: str):
if msg_id in self.__handlers and sig_name in self.__handlers[msg_id]:
del self.__handlers[msg_id][sig_name]
if len(self.__handlers[msg_id]) == 0:
del self.__handlers[msg_id]
def decode_message(self, msg_id: int, data: bytearray):
if msg_id in self.__handlers:
sigs = self.__handlers[msg_id]
for sig_name, hdl in sigs.items():
sig = self.__msgs[msg_id][1][sig_name]
r = sig.decode(data)
hdl.on_decoded(msg_id, sig_name, r)
def encode_message(self, msg_id: int, values: dict):
if msg_id in self.__handlers:
sigs = self.__handlers[msg_id]
data = bytearray(8)
for sig_name, val in values.items():
if sig_name in sigs:
sig = self.__msgs[msg_id][1][sig_name]
sig.encode(val, data)
else:
self.__logger.warning("Did not register handler for signal (#{}).{}".format(msg_id, sig_name))
sigs[sig_name].on_encoded(msg_id, data)
return dbcc

View File

@ -0,0 +1,126 @@
#include "lua.hpp"
#include "dbcc/dbc_iterator.h"
#include "dbcc/message.h"
#include "dbcc/signal.h"
#include "dbcc/version.h"
#define LUA_MODULE_NAME "dbcc"
#define LUA_DBC_ITERATOR_CLASS_NAME "DbcIterator"
// These are the definitions for the C functions that Lua will use to call the member functions.
// Consructor of 'ad::dbcc::DbcIterator'
static int lua_DbcIterator_new(lua_State* L)
{
if (lua_gettop(L) != 1)
{
luaL_pushfail(L);
return 1;
}
std::strting filePath = luaL_checkstring(L, 1);
ad::dbcc::DbcIterator** iter = reinterpret_cast<ad::dbcc::DbcIterator**>(lua_newuserdata(L, sizeof(ad::dbcc::DbcIterator**)));
*iter = new ad::dbcc::DbcIterator(filePath);
luaL_setmetatable(L, LUA_DBC_ITERATOR_CLASS_NAME);
return 1;
}
// `ad::dbcc::DbcIterator` destructor, which corresponds to the `__gc` metamethod in Lua.
int lua_DbcIterator_delete(lua_State* L)
{
ad::dbcc::DbcIterator* iter = *reinterpret_cast<ad::dbcc::DbcIterator**>(luaL_checkudata(L, 1, LUA_DBC_ITERATOR_CLASS_NAME));
delete iter;
return 0;
}
// C function corresponding to the `ad::dbcc::DbcIterator` iterator.
int lua_MyClass_index(lua_State* L)
{
ad::dbcc::DbcIterator* iter = *reinterpret_cast<ad::dbcc::DbcIterator**>(luaL_checkudata(L, 1, LUA_DBC_ITERATOR_CLASS_NAME));
int something_in = static_cast<int>(luaL_checkinteger(L, 2));
(*iter)[]->set(something_in);
return 0;
}
// C function corresponding to `MyClass::get`.
int lua_MyClass_get(lua_State* L)
{
ad::dbcc::DbcIterator* iter = *reinterpret_cast<ad::dbcc::DbcIterator**>(luaL_checkudata(L, 1, LUA_DBC_ITERATOR_CLASS_NAME));
// Push a lua object
lua_pushinteger(L, (*iter)[]);
return 1;
}
// `__newindex` metamethod for `MyClass` userdata that prevents any members from being added.
int lua_MyClass_newindex(lua_State* L)
{
return luaL_error(L, "attempt to modify a read-only object");
}
// `__newindex` metamethod for the `MyClass` table that prevents any methods from being added--I will explain more below.
int lua_MyClass_table_newindex(lua_State* L)
{
return luaL_error(L, "attempt to modify a read-only table");
}
// Function to register all the above functions for use in Lua; this gets called in `main.cpp`.
void lua_MyClass_register(lua_State* L)
{
// Create a global table that will contain all the `MyClass` methods as functions.
// Include `lua_MyClass_new` as a constructor in the form `MyClass.new`.
lua_newtable(L);
lua_pushcfunction(L, lua_MyClass_new);
lua_setfield(L, -2, "new");
// Include `MyClass::get` and `MyClass::set` in this table as well.
luaL_setfuncs(L, MyClass_methods, 0);
// Create a metatable for the global table `MyClass`--which was just created.
lua_newtable(L);
// Prevent access to the metatable.
lua_pushliteral(L, "metatable");
lua_setfield(L, -2, "__metatable");
lua_pushcfunction(L, lua_MyClass_table_newindex);
lua_setfield(L, -2, "__newindex");
// Set this second table as the metatable for the one created above.
lua_setmetatable(L, -2);
// Call the first table "MyClass" and add it to the global environment table (_ENV).
lua_setglobal(L, LUA_MYCLASS);
// Create a metatable to be used by `MyClass` objects--this is different from the above tables because it will not contain the `new` method.
luaL_newmetatable(L, LUA_MYCLASS);
// Same as before, lock the metatable.
lua_pushliteral(L, "metatable");
lua_setfield(L, -2, "__metatable");
// Add metamethods contained in the `luaL_Reg` struct `MyClass_metamethods`.
luaL_setfuncs(L, MyClass_metamethods, 0);
// Create an index--the `__index` metamethod--for the above table to use for `MyClass` objects.
lua_newtable(L);
// Add methods.
luaL_setfuncs(L, MyClass_methods, 0);
lua_setfield(L, -2, "__index");
// This pop operation is probably unnecessary since the Lua stack should be cleaned up when this function returns.
lua_pop(L, 1);
return;
}
#if defined(OS_WINDOWS)
#define __EXPORT extern "C" __declspec(dllexport)
#else
#define __EXPORT extern "C"
#endif //
__EXPORT int luaopen_dbcc(lua_State *L)
{
static const struct luaL_Reg methods[] = {
{"new", },
{NULL, NULL}
};
luaL_newlib(L, methods);
return 1;
}

116
dbcc/binding/py/__init__.py Normal file
View File

@ -0,0 +1,116 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""A simple Python3 wrapper of the ``dbcc`` library.
It contions a subset functions of the ``dbcc`` library, but also
provide some auxiliary classes to simplify the development process.
Typical usage example:
# Create a delegate to receive the encding/decoding results.
class MyDelegate(SignalDelegate):
def __init__(self):
super().__init__()
def on_encoded(self, msg_id: int, db: bytearray):
print("msg:", msg_id, ":", db)
def on_decoded(self, msg_id: int, signal_name: str, value: float):
print("msg:", msg_id, ",", signal_name, ":", value)
# Then start to process the data from log file or real-time CAN bus.
myhandler = MyDelegate()
dbc_file_path = sys.argv[1]
sp = SignalProcessor(dbc_file_path)
msg_id = 578
sp.register_delegate(msg_id, "ACCAccReqValHSC2", myhandler)
# Prepare the CAN dataframe buffer, usually, it is received from CAN bus
data = bytearray([0x01, 0xA8, 0, 0, 0, 0, 0, 0])
# Do decode and encode, then you will receive callback in handler.
sp.decode_message(msg_id, data)
sp.encode_message(msg_id, {"ACCAccReqValHSC2": -5.1})
"""
import logging
from ._dbcc import DbcParser, version
__author__ = ['"donkey" <anjingyu_ws@foxmail.com>']
__all__ = ['SignalDelegate', 'SignalProcessor']
__version__ = version()
class SignalDelegate:
"""Signal/Message encode/decode delegate, you should inherit this class and implement you own delegate."""
def on_encoded(self, msg_id: int, db: bytearray):
pass
def on_decoded(self, msg_id: int, signal_name: str, value: float):
pass
class SignalProcessor:
PLAIN_FORMAT_STRING = '[{asctime}][{levelname}][{filename}:{lineno}][{funcName}] {message}'
"""Auxiliary class, help you process the dataframe easily."""
def __init__(self, dbc_file_path: str):
self.__dp = DbcParser(dbc_file_path)
self.__msgs = {}
self.__handlers = {}
self.__logger = logging.getLogger("dbcc")
console = logging.StreamHandler()
formatter = logging.Formatter(SignalProcessor.PLAIN_FORMAT_STRING, style="{")
console.setFormatter(formatter)
console.setLevel(logging.DEBUG)
self.__logger.addHandler(console)
for msg in self.__dp:
if msg.dlc == 0:
continue
msg_entity = (msg, {})
for sig in msg:
msg_entity[1][sig.name] = sig
self.__msgs[msg.id] = msg_entity
def register_delegate(self, msg_id: int, sig_name: str, delegate: SignalDelegate) -> bool:
if msg_id in self.__msgs and sig_name in self.__msgs[msg_id][1]:
if msg_id not in self.__handlers:
self.__handlers[msg_id] = {}
self.__handlers[msg_id][sig_name] = delegate
return True
return False
def unregister_delegate(self, msg_id: int, sig_name: str):
if msg_id in self.__handlers and sig_name in self.__handlers[msg_id]:
del self.__handlers[msg_id][sig_name]
if len(self.__handlers[msg_id]) == 0:
del self.__handlers[msg_id]
def decode_message(self, msg_id: int, data: bytearray):
if msg_id in self.__handlers:
sigs = self.__handlers[msg_id]
for sig_name, hdl in sigs.items():
sig = self.__msgs[msg_id][1][sig_name]
r = sig.decode(data)
hdl.on_decoded(msg_id, sig_name, r)
def encode_message(self, msg_id: int, values: dict):
if msg_id in self.__handlers:
sigs = self.__handlers[msg_id]
data = bytearray(8)
for sig_name, val in values.items():
if sig_name in sigs:
sig = self.__msgs[msg_id][1][sig_name]
sig.encode(val, data)
else:
self.__logger.warning("Did not register handler for signal (#{}).{}".format(msg_id, sig_name))
sigs[sig_name].on_encoded(msg_id, data)

View File

@ -0,0 +1,142 @@
#include "pybind11/pybind11.h"
#include "pybind11/stl.h"
#include <sstream> /**< std::stringstream */
#include <cstring> /**< memmove */
#include "dbcc/dbc_iterator.h"
#include "dbcc/message.h"
#include "dbcc/signal.h"
#include "dbcc/version.h"
using namespace pybind11;
PYBIND11_MODULE(_dbcc, m)
{
m.def("version", &ad::dbcc::version);
enum_<ad::dbcc::ByteOrder>(m, "ByteOrder")
.value("Motorola", ad::dbcc::ByteOrder::Motorola)
.value("Intel", ad::dbcc::ByteOrder::Intel)
.export_values();
enum_<ad::dbcc::Sign>(m, "Sign")
.value("Unsigned", ad::dbcc::Sign::Unsigned)
.value("Signed", ad::dbcc::Sign::Signed)
.export_values();
enum_<ad::dbcc::Multiplexor>(m, "Multiplexor")
.value("None", ad::dbcc::Multiplexor::None)
.value("Multiplexed", ad::dbcc::Multiplexor::Multiplexed)
.value("Multiplexor", ad::dbcc::Multiplexor::Multiplexor)
.export_values();
class_<ad::dbcc::Signal::LayoutInfo>(m, "SignalLayoutInfo")
.def_readonly("bits", &ad::dbcc::Signal::LayoutInfo::bits)
.def_readonly("byte_lines", &ad::dbcc::Signal::LayoutInfo::byteLines)
.def_readonly("byte_line_range", &ad::dbcc::Signal::LayoutInfo::byteLineRange);
class_<ad::dbcc::Signal>(m, "Signal")
.def("__str__", [](ad::dbcc::Signal &sig) {
std::stringstream ss;
ss << "Signal(" << sig.name() << "[" << (sig.isFloat() ? "float" : "int") << "]: "
<< sig.startBit() << "," << sig.length() << " "
<< "(" << sig.factor() << "," << sig.offset() << ") "
<< "[" << sig.minimum() << "," << sig.maximum() << "]";
return ss.str();
})
.def("__repr__", [](ad::dbcc::Signal &s) {
std::stringstream ss;
ss << "<dbcc.Signal name: " << s.name() << ">";
return ss.str();
})
.def("encode", [](ad::dbcc::Signal &sig, double value, bytearray &ba) {
Py_buffer buffer;
if (PyObject_GetBuffer(ba.ptr(), &buffer, PyBUF_WRITABLE) < 0)
{
return;
}
sig.encode(value, reinterpret_cast<uint8_t *>(buffer.buf), buffer.len);
PyBuffer_Release(&buffer);
})
.def("decode", [](ad::dbcc::Signal &sig, bytearray &ba) -> double {
double ret = 0;
Py_buffer buffer;
if (PyObject_GetBuffer(ba.ptr(), &buffer, PyBUF_SIMPLE) == 0)
{
sig.decode(reinterpret_cast<uint8_t *>(buffer.buf), buffer.len, ret);
PyBuffer_Release(&buffer);
}
return ret;
})
.def("bits_layout", [](ad::dbcc::Signal &sig) -> ad::dbcc::Signal::LayoutInfo {
ad::dbcc::Signal::LayoutInfo ret;
sig.bitsLayout(ret);
return ret;
})
.def_property_readonly("name", &ad::dbcc::Signal::name)
.def_property_readonly("byte_order", &ad::dbcc::Signal::byteOrder)
.def_property_readonly("start_bit", &ad::dbcc::Signal::startBit)
.def_property_readonly("length", &ad::dbcc::Signal::length)
.def_property_readonly("sign", &ad::dbcc::Signal::sign)
.def_property_readonly("min", &ad::dbcc::Signal::minimum)
.def_property_readonly("max", &ad::dbcc::Signal::maximum)
.def_property_readonly("factor", &ad::dbcc::Signal::factor)
.def_property_readonly("offset", &ad::dbcc::Signal::offset)
.def_property_readonly("unit", &ad::dbcc::Signal::unit)
.def_property_readonly("is_float", &ad::dbcc::Signal::isFloat)
.def_property_readonly("multiplexor", &ad::dbcc::Signal::multiplexor)
.def_property_readonly("multiplexed_num", &ad::dbcc::Signal::multiplexedNumber)
.def_property_readonly("to", &ad::dbcc::Signal::to);
class_<ad::dbcc::Message>(m, "Message")
.def("__len__", &ad::dbcc::Message::size)
/* Essential: keep object alive while iterator exists */
.def("__iter__", [](ad::dbcc::Message &s) {
return make_iterator(s.begin(), s.end());
}, keep_alive<0, 1>())
.def("__getitem__", [](ad::dbcc::Message &s, size_t i) {
if (i >= s.size())
{
throw index_error();
}
return s[i];
})
.def("__str__", [](ad::dbcc::Message &s) {
std::stringstream ss;
ss << "Message(" << s.name() << "[0x" << std::hex << s.id() << std::dec << ", " << s.id() << "])";
return ss.str();
})
.def("__repr__", [](ad::dbcc::Message &s) {
std::stringstream ss;
ss << "<dbcc.Message name: " << s.name() << ", id: 0x" << std::hex << s.id()
<< ", " << std::dec << s.id() << ">";
return ss.str();
})
.def_property_readonly("name", &ad::dbcc::Message::name)
.def_property_readonly("id", &ad::dbcc::Message::id)
.def_property_readonly("dlc", &ad::dbcc::Message::dlc)
.def_property_readonly("from", &ad::dbcc::Message::from)
.def_property_readonly("to", &ad::dbcc::Message::to);
class_<ad::dbcc::DbcIterator>(m, "DbcParser")
.def(init<const std::string &>())
.def("__len__", &ad::dbcc::DbcIterator::size)
/* Essential: keep object alive while iterator exists */
.def("__iter__", [](ad::dbcc::DbcIterator &s) {
return make_iterator(s.begin(), s.end());
}, keep_alive<0, 1>())
.def("__getitem__", [](ad::dbcc::DbcIterator &s, size_t i) {
if (i >= s.size())
{
throw index_error();
}
return s[i];
});
}

View File

@ -0,0 +1,75 @@
#!/usr/bin/env python3
# -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8
import os
import sys
import platform
from setuptools import setup, find_packages
from setuptools.dist import Distribution
import sysconfig
__author__ = ['"donkey" <anjingyu_ws@foxmail.com>']
class BinaryDistribution(Distribution):
"""Distribution which always forces a binary package with platform name"""
def has_ext_modules(foo):
return True
def ext_name():
name = "_dbcc"
if platform.system() == "Windows":
name = name + ".pyd"
elif platform.system() == "Darwin":
name = name + ".dylib"
else:
name = name + ".so"
return name
def other_deps():
if platform.system() == "Windows":
result = []
script_dir = os.path.dirname(os.path.abspath(__file__))
for file in os.listdir(os.path.join(script_dir, 'dbcc')):
file_lower = file.lower()
if file_lower.endswith(".dll") or file_lower.endswith(".pyd"):
result.append(file)
return result
else:
return []
def main():
setup(name='dbcc',
version='@DBCC_VERSION@',
description="DBCC Python extension",
long_description="DBCC Python extension",
keywords="DBCC",
author="donkey",
author_email="<anjingyu_ws@foxmail.com>",
classifiers=[
'Programming Language :: Python :: 3.6',
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Operating System :: Microsoft :: Windows' if platform.system() == "Windows" else 'Operating System :: POSIX :: Linux'
],
python_requires="~=@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@",
license="MIT",
packages=find_packages(),
package_data={
"dbcc": [ext_name()] + other_deps()
},
# The following lines are the same
# distclass=BinaryDistribution,
has_ext_modules=lambda: True,
include_package_data=True,
zip_safe=False)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,23 @@
[package]
name = "rdbcc"
version = "0.1.0"
authors = ["donkey <anjingyu_ws@foxmail.com>"]
license = "WTFPL 2"
description = "RUST binding of dbcc library"
repository = "http://39.105.213.233:3000/donkey/dbcc.git"
edition = "2021"
publish = false
build = "build.rs"
links = "dbcc"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = ""
[build-dependencies]
dunce = "1.0.2"
[lib]
# Create static library only
crate-type = ["staticlib"]

View File

@ -0,0 +1,21 @@
extern crate dunce;
extern crate cc;
use std::{env, path::PathBuf};
fn main() {
let library_name = "dbcc";
let root = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
// let library_dir = dunce::canonicalize(root.join("src")).unwrap();
let dbcc_library_path = root.parent().unwrap().parent().unwrap().to_str().unwrap();
println!("cargo:rustc-link-lib=static={}", library_name);
println!("cargo:rustc-link-search=native={}", dbcc_library_path);
println!("cargo:rerun-if-changed=src/rust_wrapper.cpp");
cc::Build::new()
.cpp(true)
.file("src/doubler.cpp")
.compile("libdbcc.a");
}

View File

@ -0,0 +1,87 @@
use std::{
io,
fmt
};
extern crate libc;
use libc::{c_int};
enum ByteOrder {
Motorola,
Intel
}
enum Sign {
Unsigned,
Signed
}
enum Multiplexor {
None,
Multiplexed,
Multiplexor
}
extern "C" {
fn new_dbc_iterator() -> *mut DbcIterator;
fn delete_dbc_iterator(p: *mut DbcIterator);
}
#[derive(Debug, PartialEq)]
pub struct DbcParser {
raw: *mut DbcIterator
}
impl DbcParser {
fn new() -> DbcParser {
unsafe { DbcParser { raw: new_dbc_iterator() } }
}
}
impl Drop for DbcParser {
fn drop(&mut self) {
unsafe {
delete_dbc_iterator(self.raw);
}
}
}
#[derive(Debug, PartialEq)]
pub struct Message {
}
#[derive(Debug, PartialEq)]
pub struct Signal {
}
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &byte in &self.bytes {
f.write_fmt(format_args!("{:02x}", byte))?;
}
Ok(())
}
}
impl fmt::Display for Signal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &byte in &self.bytes {
f.write_fmt(format_args!("{:02x}", byte))?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,20 @@
#pragma once
#include "dbcc/dbc_iterator.h"
#include "dbcc/message.h"
#include "dbcc/signal.h"
#include "dbcc/signal_delegate.h"
#include "dbcc/signal_processor.h"
// C interface wrapper
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
ad::dbcc::SignalProcessor *SignalProcessor_new(const char *filePath);
void SignalProcessor_delete(ad::dbcc::SignalProcessor * o);
#if defined(__cplusplus)
}
#endif /* __cplusplus */

29
dbcc/include/dbcc/can.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <cstdint> /**< uint8_t */
namespace ad {
namespace dbcc {
enum class ByteOrder
{
Motorola, ///< 0 big-endbian
Intel ///< 1 little-endbian
};
enum class Sign
{
Unsigned,
Signed
};
enum class Multiplexor
{
None,
Multiplexed,
Multiplexor
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,69 @@
#pragma once
#include <iostream> /**< std::istream std::ostream */
#include <string> /**< std::string */
#include <vector> /**< std::vector */
#include <iosfwd>
#include <map> /**< std::map */
#include "dbcc/message.h"
namespace ad {
namespace dbcc {
class DbcIterator
{
public:
typedef std::vector<Message> Messages;
typedef Messages::const_iterator const_iterator;
typedef Messages::iterator iterator;
typedef Messages::reference reference;
typedef Messages::const_reference const_reference;
public:
explicit DbcIterator(const std::string &filePath);
explicit DbcIterator(std::istream &stream);
friend std::ostream &operator<<(std::ostream &out, DbcIterator &dbc);
inline iterator begin()
{
return m_messages.begin();
}
inline iterator end()
{
return m_messages.end();
}
inline reference operator[](std::size_t idx)
{
return m_messages[idx];
}
inline size_t size() const
{
return m_messages.size();
}
inline iterator find(uint32_t msgId)
{
if (m_messageIndex.find(msgId) != m_messageIndex.end())
{
return m_messages.begin() + m_messageIndex[msgId];
}
return m_messages.end();
}
private:
void _parse(std::istream &stream);
private:
Messages m_messages;
std::map<uint32_t, uint32_t> m_messageIndex;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,75 @@
#pragma once
#include <string> /**< std::string */
#include <vector> /**< std::vector */
#include "dbcc/signal.h"
namespace ad {
namespace dbcc {
namespace helper {
/**
* @brief
* Helper class, help generater to generate C/C++ source code.
*/
class SignalHelper
{
public:
enum ShiftDirection
{
Left,
Right
};
struct Segment
{
int index;
int shift;
int mask;
ShiftDirection direction;
};
public:
SignalHelper(const ad::dbcc::Signal &signal) : m_signal(signal) {}
inline int typeLength() const
{
uint16_t len = m_signal.length();
if (len <= 8)
{
return 8;
}
else if (len <= 16)
{
return 16;
}
else if (len <= 32)
{
return 32;
}
return 64;
}
int typeLengthBsf() const;
const std::string &operationTypeName() const;
const std::string &typeName() const;
const std::string &conversionTypeSuffix() const;
const std::string &typeSuffix() const;
std::vector<Segment> segments(bool invertShift=false) const;
std::string unpack2Code(const std::string &name="", const std::string &prefix="", int indentLevel=1);
std::string pack2Code(const std::string &name="", const std::string &prefix="", int indentLevel=1);
private:
const ad::dbcc::Signal &m_signal;
};
} // namespace helper
} // namespace dbcc
} // namespace ad

139
dbcc/include/dbcc/message.h Normal file
View File

@ -0,0 +1,139 @@
#pragma once
#include <cstdint> /**< */
#include <string> /**< std::string */
#include <set> /**< std::set */
#include <vector> /**< std::vector */
#include <iosfwd> /**< */
#if (__cplusplus >= 201103L)
#include <unordered_map>
#else
#include <map>
#endif
#include "dbcc/signal.h"
#if (__cplusplus >= 201103L)
typedef std::unordered_map<std::string, uint32_t> SignalIndexMap;
#else
typedef std::map<std::string, uint32_t> SignalIndexMap;
#endif
namespace ad {
namespace dbcc {
/**
* @brief
* Class representing a \c Message in the DBC file.
* It allows its user to query data and to iterate over the signals
* contained in the Message.
*/
class Message
{
public:
typedef std::vector<Signal> Signals;
typedef Signals::const_iterator const_iterator;
typedef Signals::iterator iterator;
typedef Signals::const_reference const_reference;
typedef Signals::reference reference;
friend std::istream &operator>>(std::istream &in, Message &msg);
/// \brief The name of this \c Message
inline const std::string name() const
{
return m_name;
}
/// \brief The CAN ID assigned to this specific message
inline uint32_t id() const
{
return m_id;
}
/// \brief
/// The length of this message in bytes.
/// Allowed values are between 0 and 8
inline std::size_t dlc() const
{
return m_dlc;
}
/// \brief
/// String containing the name of the sender of thsi message if one exists in the DB
inline const std::string &from() const
{
return m_from;
}
/// \brief
/// List contains all signals which are present in this message.
std::set<std::string> to() const;
inline iterator begin()
{
return m_signals.begin();
}
inline iterator end()
{
return m_signals.end();
}
inline reference operator[](std::size_t idx)
{
return m_signals[idx];
}
inline size_t operator[](const std::string &name)
{
if (m_indexMap.find(name) == m_indexMap.end())
{
return InvalidIndex;
}
return m_indexMap[name];
}
inline bool contains(const std::string &name)
{
if (m_indexMap.find(name) == m_indexMap.end())
{
return false;
}
return true;
}
inline size_t size() const
{
return m_signals.size();
}
private:
inline void appendSignal(Signal &sig)
{
sig.setMessageId(m_id);
m_signals.push_back(sig);
// Append index
m_indexMap[sig.name()] = static_cast<uint32_t>(m_signals.size() - 1);
}
private:
std::string m_name;
uint32_t m_id;
size_t m_dlc;
std::string m_from;
std::set<std::string> m_to;
Signals m_signals;
SignalIndexMap m_indexMap;
};
} // namespace dbcc
} // namespace ad

244
dbcc/include/dbcc/signal.h Normal file
View File

@ -0,0 +1,244 @@
#pragma once
#include <iostream> /**< std::istream */
#include <cstdint> /**< uint16_t */
#include <memory> /**< std::shared_ptr */
#include <vector> /**< std::vector */
#include <set> /**< std::set */
#include <string> /**< std::string */
#include <iosfwd> /**< */
#include "dbcc/can.h"
#include "dbcc/signal_delegate.h"
namespace ad {
namespace dbcc {
const size_t InvalidIndex = static_cast<size_t>(-1);
class Message;
/**
* @brief
* This class represents a \c Signal contained in a \c Messsage in a DBC file.
* One can Query all the necessary information from this class to define a \c Signal.
*/
class Signal
{
struct CompiledSignalParameter
{
uint32_t index;
uint32_t shift; ///< TODO(donkey): Optimization, use the highest bit to indicate the shift direction.
uint32_t mask;
uint32_t left; ///< 0: right shift, 1: left shift
friend std::ostream &operator<<(std::ostream &out, CompiledSignalParameter &p);
};
typedef std::vector<CompiledSignalParameter> Parameters;
typedef std::set<std::string> ToList;
public:
struct LayoutInfo
{
std::vector<int> bits;
std::vector<int> byteLines;
std::vector< std::pair<int, int> > byteLineRange;
};
public:
Signal() : m_msgId(0), m_signedLengthMask(0) {}
virtual ~Signal();
/// \brief The name of the \c Signal in the DBC file
inline const std::string &name() const
{
return m_name;
}
/// \brief \c ByteOrder of the \c Signal
inline ByteOrder byteOrder() const
{
return m_byteOrder;
}
/// \brief
/// The start bit inside the \c Message of this \c Signal.
/// Allowed values are 0-63
inline uint16_t startBit() const
{
return m_startBit;
}
/// \brief
/// The length of the \c Signal.
/// It can be anything between 1 and 64 for CAN and CANFD message,
inline uint16_t length() const
{
return m_length;
}
/// \brief If the data contained in the \c Signal is signed or unsigned data.
inline Sign sign() const
{
return m_sign;
}
/// \breif Depending on the information given above one can calculate the minimum of this \c Signal.
inline double minimum() const
{
return m_minimum;
}
/// \brief Depending on the information given above one can calculate the maximum of this \c Signal.
inline double maximum() const
{
return m_maximum;
}
/// \brief
/// The factor for calculating the physical value: phys = digits * factor + offset
inline double factor() const
{
return m_factor;
}
/// \brief
/// The offset for calculating the physical value: phys = digits * factor + offset
inline double offset() const
{
return m_offset;
}
/// \brief
/// The value is float/double or not.
inline bool isFloat() const
{
return m_isFloat;
}
/// \brief String containing an associated unit.
inline const std::string &unit() const
{
return m_unit;
}
/// \brief
/// Contains weather the \c Signal is \c Multiplexed and if it is,
/// \c multiplexNumber contains multiplex number.
inline Multiplexor multiplexor() const
{
return m_multiplexor;
}
/// \brief
/// Contains the multiplex number if the \c Signal is multiplexed
inline uint16_t multiplexedNumber() const
{
return m_multiplexedNumber;
}
/// \brief
/// Contains to which control units in the CAN-Network the \c Singal shall be sent.
inline ToList to() const
{
return m_to;
}
// NOTE(donkey):
// Here we store the user delegates in each signal instance,
// The reason why this is done here is to facilitate the \c SignalProcessor to obtain the delegates.
/// \brief Set encode delegate for this signal.
inline void setDelegate(std::shared_ptr<SignalEncodeDelegate> &delegate)
{
m_encodeDelegate = delegate;
}
/// \brief Set decode delegate for this signal.
inline void setDelegate(std::shared_ptr<SignalDecodeDelegate> &delegate)
{
m_decodeDelegate = delegate;
}
/// \brief Get the encode delegate shared pointer
std::shared_ptr<SignalEncodeDelegate> getEncodeDelegate()
{
return m_encodeDelegate;
}
/// \brief Get the decode delegate shared pointer
std::shared_ptr<SignalDecodeDelegate> getDecodeDelegate()
{
return m_decodeDelegate;
}
/// \brief
/// Pack the signal value to CAN buffer format.
bool encode(const ParsedValue &pv, uint8_t *data, size_t length);
/// \brief
/// Unpack the signal value from a CAN buffer.
bool decode(const uint8_t *data, size_t length, ParsedValue &pv);
/// \brief
/// Pack the signal value to CAN buffer format.
bool encode(double value, uint8_t *data, size_t length);
/// \brief
/// Unpack the signal value from a CAN buffer.
bool decode(const uint8_t *data, size_t length, double &value);
/// \brief
/// Return the bits layout of this signal
bool bitsLayout(LayoutInfo &info);
inline uint32_t getMessageId() const
{
return m_msgId;
}
friend std::istream& operator>>(std::istream &in, Signal &sig);
friend std::ostream& operator<<(std::ostream &out, CompiledSignalParameter &p);
friend std::ostream& operator<<(std::ostream &out, LayoutInfo &p);
friend std::ostream& operator<<(std::ostream &out, Signal &cs);
private:
bool compile();
inline void setMessageId(uint32_t msgId)
{
m_msgId = msgId;
}
private:
uint32_t m_msgId;
std::string m_name;
ByteOrder m_byteOrder;
uint16_t m_startBit;
uint16_t m_length;
Sign m_sign;
double m_minimum;
double m_maximum;
double m_factor;
double m_offset;
bool m_isFloat;
std::string m_unit;
Multiplexor m_multiplexor;
uint16_t m_multiplexedNumber;
ToList m_to;
uint32_t m_signedLengthMask;
Parameters m_params;
std::shared_ptr<SignalEncodeDelegate> m_encodeDelegate;
std::shared_ptr<SignalDecodeDelegate> m_decodeDelegate;
friend class Message;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,54 @@
#pragma once
#include <cstdint> /**< int32_t uint32_t */
#include <vector> /**< std::vector */
#include <string> /**< std::string */
#include <utility> /**< std::pair */
namespace ad {
namespace dbcc {
struct ParsedValue
{
bool isInteger;
union
{
int32_t i;
float f;
};
};
class SignalEncodeDelegate
{
public:
/**
* @brief Callback after encoding a signal.
*
* @param msgId The corresponding message ID.
* @param signalName The signal name has been encoded.
* @param data Stores the encoded data, the encoding processor only modified the corresponding bits of this signal.
* @param length The buffer length.
* @param pv The value that has been encoded.
* @param ended If this signal is the last one that can be parsed(you are registered), this parameter will be set \c true, otherwise it will be set \c false.
* @return bool If returns \c true, the encoding process will continue, if returns \c false, the encoding process will be interrupted for current message.
*/
virtual bool onEncoded(uint32_t msgId, const std::string &signalName, uint8_t *data, size_t length, const ParsedValue &pv, bool ended) = 0;
};
class SignalDecodeDelegate
{
public:
/**
* @brief Callback after decoding a signal.
*
* @param msgId The message ID.
* @param signalName The signal name.
* @param pv The decoded value of this signal.
*/
virtual void onDecoded(uint32_t msgId, const std::string &signalName, const ParsedValue &pv) = 0;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,164 @@
#pragma once
#include <cassert>
#include <cstdint>
#include <vector> /**< std::vector */
#include <utility> /**< std::pair */
#include <memory> /**< std:;shared_ptr */
#include <functional> /**< std::reference_wrapper */
#include <algorithm> /**< std::find_if */
#if (__cplusplus >= 201103L)
#include <unordered_map>
#else
#include <map>
#endif
#include "dbcc/dbc_iterator.h"
#include "dbcc/signal_delegate.h"
namespace ad {
namespace dbcc {
#if (__cplusplus >= 201103L)
typedef std::unordered_map< uint32_t, std::pair< std::reference_wrapper<ad::dbcc::Message>, std::vector<size_t> > > MessageMap;
typedef std::unordered_map<std::string, double> SignalValuesMap;
#else
typedef std::map< uint32_t, std::pair< std::reference_wrapper<ad::dbcc::Message>, std::vector<size_t> > > MessageMap;
typedef std::map<std::string, double> SignalValuesMap;
#endif
typedef std::vector< std::pair< std::string, const ParsedValue> > SignalValues;
typedef std::vector< std::pair< std::string, double> > SignalValuesVector;
class SignalProcessor
{
public:
SignalProcessor(ad::dbcc::DbcIterator &dbcIter)
: m_dbcIter(dbcIter)
{}
~SignalProcessor() {}
inline bool registerDelegate(uint32_t msgId, const std::string &signalName, std::shared_ptr<SignalEncodeDelegate> &delegate)
{
return _registerDelegate(msgId, signalName, m_encoderMap, delegate);
}
inline bool registerDelegate(uint32_t msgId, const std::string &signalName, std::shared_ptr<SignalDecodeDelegate> &delegate)
{
return _registerDelegate(msgId, signalName, m_decoderMap, delegate);
}
inline bool unregisterEncodeDelegate(uint32_t msgId, const std::string &signalName)
{
return _unregisterDelegate(msgId, signalName, m_encoderMap);
}
inline bool unregisterDecodeDelegate(uint32_t msgId, const std::string &signalName)
{
return _unregisterDelegate(msgId, signalName, m_decoderMap);
}
inline void unregisterAll()
{
m_encoderMap.clear();
m_decoderMap.clear();
}
bool decodeMessage(uint32_t msgId, const uint8_t *data, size_t length);
bool encodeMessage(uint32_t msgId, const SignalValues &pvs, uint8_t *data, size_t length);
bool encodeMessage(uint32_t msgId, const SignalValuesVector &pvs, uint8_t *data, size_t length);
bool encodeMessage(uint32_t msgId, const SignalValuesMap &pvs, uint8_t *data, size_t length);
private:
size_t _signalIndex(uint32_t msgId, const std::string &signalName);
template <typename DelegateType, typename MapType>
bool _registerDelegate(uint32_t msgId, const std::string &signalName, MapType& mt, DelegateType &delegate)
{
size_t idx = _signalIndex(msgId, signalName);
if (idx == InvalidIndex)
{
return false;
}
auto msgIter = m_dbcIter.find(msgId);
if (mt.find(msgId) == mt.end())
{
mt.insert({msgId, {std::ref(*msgIter), std::vector<size_t>{}}});
}
else
{
assert(mt.at(msgId).first.get().id() == msgId);
}
auto &ev = mt.at(msgId).second;
auto fr = std::find_if(ev.begin(), ev.end(), [=](const size_t &v) {
return v == idx;
});
if (fr == ev.end())
{
ev.emplace_back(idx);
}
mt.at(msgId).first.get()[idx].setDelegate(delegate);
return true;
}
template <typename MapType>
bool _unregisterDelegate(uint32_t msgId, const std::string &signalName, MapType& mt)
{
size_t idx = _signalIndex(msgId, signalName);
if (idx == InvalidIndex)
{
return false;
}
if (mt.find(msgId) == mt.end())
{
return false;
}
else
{
assert(mt.at(msgId).first.get().id() == msgId);
}
auto &dv = mt.at(msgId).second;
auto fr = std::find_if(dv.begin(), dv.end(), [=](const size_t &v) {
return v == idx;
});
if (fr == dv.end())
{
return false;
}
else
{
dv.erase(fr);
}
if (dv.size() == 0)
{
mt.erase(msgId);
}
return true;
}
private:
MessageMap m_encoderMap;
MessageMap m_decoderMap;
ad::dbcc::DbcIterator &m_dbcIter;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,11 @@
#pragma once
#include <string> /**< std::string */
namespace ad {
namespace dbcc {
std::string version();
} // namespace dbcc
} // namespace ad

1
dbcc/ref/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*/

13
dbcc/ref/README.md Normal file
View File

@ -0,0 +1,13 @@
# References
Run the following command to get the reference repositories:
``` shell
git clone --depth 1 https://ghproxy.com/https://github.com/ezaquarii/bison-flex-cpp-example
git clone --depth 1 https://ghproxy.com/https://github.com/remusao/Bison-Flex-CPP-template
git clone --depth 1 https://ghproxy.com/https://github.com/telehan/cantools-blf
git clone --depth 1 https://ghproxy.com/https://github.com/remusao/CPP_Coding_Style_Checker
git clone --depth 1 https://ghproxy.com/https://github.com/gaflach/flex-bison-cpp
git clone --depth 1 https://ghproxy.com/https://github.com/sunxfancy/flex-bison-examples
git clone --depth 1 https://github.com/protobufjs/protobuf.js.git
```

9
dbcc/ref/clone.sh Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
git clone --depth 1 https://ghproxy.com/https://github.com/ezaquarii/bison-flex-cpp-example
git clone --depth 1 https://ghproxy.com/https://github.com/remusao/Bison-Flex-CPP-template
git clone --depth 1 https://ghproxy.com/https://github.com/telehan/cantools-blf
# git clone --depth 1 https://ghproxy.com/https://github.com/remusao/CPP_Coding_Style_Checker
git clone --depth 1 https://ghproxy.com/https://github.com/gaflach/flex-bison-cpp
git clone --depth 1 https://ghproxy.com/https://github.com/sunxfancy/flex-bison-examples
git clone --depth 1 https://github.com/protobufjs/protobuf.js.git

15
dbcc/ref/list_all.sh Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
#
# List all the address of git repo
#
# Author: donkey <anjingyu_ws@foxmail.com>
for d in $(ls $PWD/); do
if [ -d "$PWD/$d" ]; then
pushd "$PWD/$d" 1>/dev/null 2>&1
_REPO_URL=$(git remote -v)
echo "git clone --depth 1 $(echo $_REPO_URL | cut -d ' ' -f 2)"
popd 1>/dev/null 2>&1
fi
done

19
dbcc/scripts/rdbcc.sh Normal file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
#
# Create rdbcc project and build
#
# Author: anjingyu_ws@foxmail.com
readonly CUR_DIR=$(cd `dirname $0`; pwd)
readonly PROJ_DIR=$(dirname ${CUR_DIR})
readonly BINDING_DIR="$PROJ_DIR/binding"
# cargo install cargo-expand
# cargo expand ::ffi
# cargo install cxxbridge-cmd
# cxxbridge src/lib.rs
pushd $BINDING_DIR 1>/dev/null 2>&1
cargo new --name rdbcc --lib rust
popd 1>/dev/null 2>&1

0
dbcc/source/c_wrapper.c Normal file
View File

143
dbcc/source/dbc.l Normal file
View File

@ -0,0 +1,143 @@
%{
#include <iostream>
#include <cstdlib>
#include <string>
#include "dbc_scanner.h"
/*
#undef YY_DECL
#define YY_DECL int dbcc::DbcScanner::yylex(dbcc::DbcParser::semantic_type * const lval, dbcc::DbcParser::location_type *loc)
using token = dbcc::DbcParser::token;
#define yyterminate() return token::END
#define YY_NO_UNISTD_H
#define YY_USER_ACTION loc->step(); loc->columns(yyleng);
*/
%}
%option nodefault
%option yyclass="dbcc::DbcScanner"
%option noyywrap
%option c++
%option prefix="dbcc_"
%x MULTI_LINE_COMMENT
%x SINGLE_LINE_COMMENT
WS [ \t\b\f\r]+
DECNUMBER [-+]?[0-9]+
HEXNUMBER 0x[0-9a-fA-F]+
DOUBLE [-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?
ID [a-zA-Z_]([_a-zA-Z0-9]*)?
STRING \"([^"\\]|(\\.))*\"
NL [\n\r]
%%
%{
/* Executed at the beginning of yylex */
yylval = lval;
%}
"//"[^\n]* ;
"VERSION" { return dbcc::DbcParser::token::T_VERSION; }
"BO_" { return dbcc::DbcParser::token::T_BO; }
"BS_" { return dbcc::DbcParser::token::T_BS; }
"BU_" { return dbcc::DbcParser::token::T_BU; }
"SG_" { return dbcc::DbcParser::token::T_SG; }
"EV_" { return dbcc::DbcParser::token::T_EV; }
"SIG_VALTYPE_" { return dbcc::DbcParser::token::T_SIG_VALTYPE; }
"NS_" { return dbcc::DbcParser::token::T_NS; }
"INT" { return dbcc::DbcParser::token::T_INT; }
"FLOAT" { return dbcc::DbcParser::token::T_FLOAT; }
"NAN" { return dbcc::DbcParser::token::T_NAN; }
"STRING" { return dbcc::DbcParser::token::T_STRING; }
"ENUM" { return dbcc::DbcParser::token::T_ENUM; }
"HEX" { return dbcc::DbcParser::token::T_HEX; }
"NS_DESC_" { return dbcc::DbcParser::token::T_NS_DESC; }
"CM_" { return dbcc::DbcParser::token::T_CM; }
"BA_DEF_" { return dbcc::DbcParser::token::T_BA_DEF; }
"BA_" { return dbcc::DbcParser::token::T_BA; }
"VAL_" { return dbcc::DbcParser::token::T_VAL; }
"CAT_DEF_" { return dbcc::DbcParser::token::T_CAT_DEF; }
"CAT_" { return dbcc::DbcParser::token::T_CAT; }
"FILTER" { return dbcc::DbcParser::token::T_FILTE; }
"BA_DEF_DEF_" { return dbcc::DbcParser::token::T_BA_DEF_DEF; }
"EV_DATA_" { return dbcc::DbcParser::token::T_EV_DATA; }
"ENVVAR_DATA_" { return dbcc::DbcParser::token::T_ENVVAR_DATA; }
"SGTYPE_" { return dbcc::DbcParser::token::T_SGTYPE; }
"SGTYPE_VAL_" { return dbcc::DbcParser::token::T_SGTYPE_VAL; }
"BA_DEF_SGTYPE_" { return dbcc::DbcParser::token::T_BA_DEF_SGTYPE; }
"BA_SGTYPE_" { return dbcc::DbcParser::token::T_BA_SGTYPE; }
"SIG_TYPE_REF_" { return dbcc::DbcParser::token::T_SIG_TYPE_REF; }
"VAL_TABLE_" { return dbcc::DbcParser::token::T_VAL_TABLE; }
"SIG_GROUP_" { return dbcc::DbcParser::token::T_SIG_GROUP; }
"SIGTYPE_VALTYPE_" { return dbcc::DbcParser::token::T_SIGTYPE_VALTYPE; }
"BO_TX_BU_" { return dbcc::DbcParser::token::T_BO_TX_BU; }
"BA_DEF_REL_" { return dbcc::DbcParser::token::T_BA_DEF_REL; }
"BA_REL_" { return dbcc::DbcParser::token::T_BA_REL; }
"BA_DEF_DEF_REL_" { return dbcc::DbcParser::token::T_BA_DEF_DEF_REL; }
"BU_SG_REL_" { return dbcc::DbcParser::token::T_BU_SG_REL; }
"BU_EV_REL_" { return dbcc::DbcParser::token::T_BU_EV_REL; }
"BU_BO_REL_" { return dbcc::DbcParser::token::T_BU_BO_REL; }
"SG_MUL_VAL_" { return dbcc::DbcParser::token::T_SG_MUL_VAL; }
"DUMMY_NODE_VECTOR"[0-3] {
yylval.number = yytext[17]-'0';
return T_DUMMY_NODE_VECTOR;
}
{NL} { loc->lines(); }
{WS} { ; }
{ID} {
yylval->build<std::string>(yytext);
return dbcc::DbcParser::token::T_ID;
}
{STRING} {
std::string v = std::string(yytext);
if (v.size() > 2) {
yytext[v.size() - 1] = 0;
yylval->build<std::string>(yytext + 1);
} else {
/* Empty string */
yylval->build<std::string>("");
}
return dbcc::DbcParser::token::T_STRING_VAL;
}
{DECNUMBER} {
yylval->build<int64_t>(std::atoi(yytext));
return dbcc::DbcParser::token::T_INT_VAL;
}
{HEXNUMBER} {
yylval->build<int64_t>(std::strtol(yytext, NULL, 16));
return dbcc::DbcParser::token::T_INT_VAL;
}
{DOUBLE} {
yylval->build<double>(std::strtod(yytext, NULL));
return dbcc::DbcParser::token::T_DOUBLE;
}
":" { return dbcc::DbcParser::token::T_COLON; }
";" { return dbcc::DbcParser::token::T_SEMICOLON; }
"|" { return dbcc::DbcParser::token::T_SEP; }
"," { return dbcc::DbcParser::token::T_COMMA; }
"@" { return dbcc::DbcParser::token::T_AT; }
"+" { return dbcc::DbcParser::token::T_PLUS; }
"-" { return dbcc::DbcParser::token::T_MINUS; }
"[" { return dbcc::DbcParser::token::T_BOX_OPEN; }
"]" { return dbcc::DbcParser::token::T_BOX_CLOSE; }
"(" { return dbcc::DbcParser::token::T_PAR_OPEN; }
")" { return dbcc::DbcParser::token::T_PAR_CLOSE; }
<<EOF>> { yyterminate(); }
. { return yytext[0]; }
%%

1019
dbcc/source/dbc.y Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
#include "dbcc/dbc_iterator.h"
#include <limits>
#include <fstream>
namespace ad {
namespace dbcc {
DbcIterator::DbcIterator(const std::string &filePath)
{
std::ifstream file(filePath);
if (file)
{
_parse(file);
}
file.close();
}
DbcIterator::DbcIterator(std::istream &stream)
{
_parse(stream);
}
std::ostream &operator<<(std::ostream &out, DbcIterator &dbc)
{
for (auto &msg : dbc)
{
out << msg.name() << " " << msg.id() << std::endl;
for (auto &sig : msg)
{
out << "Signal: " << sig.name() << " [" << (sig.isFloat() ? "float" : "int") << "] ";
out << "To: ";
for (auto to : sig.to())
{
out << to << ", ";
}
out << sig.startBit() << "," << sig.length() << std::endl;
out << "(" << sig.factor() << ", " << sig.offset() << ")" << std::endl;
out << "[" << sig.minimum() << ", " << sig.maximum() << "]" << std::endl;
if (sig.multiplexor() == Multiplexor::Multiplexed)
{
out << "#" << sig.multiplexedNumber() << "#" << std::endl;
}
else if (sig.multiplexor() == Multiplexor::Multiplexor)
{
out << "+Multiplexor+" << std::endl;
}
out << std::endl;
}
}
return out;
}
void DbcIterator::_parse(std::istream &stream)
{
m_messages.clear();
do {
Message msg;
stream >> msg;
if (stream.fail())
{
stream.clear();
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
else
{
m_messages.emplace_back(msg);
m_messageIndex[msg.id()] = static_cast<uint32_t>(m_messages.size() - 1);
}
} while (!stream.eof());
}
} // namespace dbcc
} // namespace ad

41
dbcc/source/dbc_scanner.h Normal file
View File

@ -0,0 +1,41 @@
#program once
/**
* Generated Flex class name is yyFlexLexer by default. If we want to use more flex-generated
* classes we should name them differently. See scanner.l prefix option.
*
* Unfortunately the implementation relies on this trick with redefining class name
* with a preprocessor macro. See GNU Flex manual, "Generating C++ Scanners" section
*/
#if ! defined(yyFlexLexerOnce)
#undef yyFlexLexer
#define yyFlexLexer dbcc_FlexLexer // the trick with prefix; no namespace here :(
#include <FlexLexer.h>
#endif
// Scanner method signature is defined by this macro. Original yylex() returns int.
// Sinice Bison 3 uses symbol_type, we must change returned type. We also rename it
// to something sane, since you cannot overload return type.
#undef YY_DECL
#define YY_DECL dbcc::DbcParser::symbol_type dbcc::DbcScanner::GetNextToken()
#include "parser.hpp" // this is needed for symbol_type
namespace dbcc {
// Forward declare interpreter to avoid include. Header is added inimplementation file.
class DbcInterpreter;
class DbcScanner : public yyFlexLexer
{
public:
DbcScanner(Interpreter &driver) : driver_(driver) {}
virtual ~DbcScanner() {}
virtual dbcc::DbcParser::symbol_type GetNextToken();
private:
Interpreter &driver_;
};
} // namespace dbcc

View File

@ -0,0 +1,348 @@
#include <iostream> /**< */
#include <iomanip> /**< std::setw std::setfill */
#include <string> /**< std::string */
#include <sstream> /**< std;:stringstream */
#include <vector> /**< std::vector */
#include <algorithm> /**< std::find_if */
#include "dbcc/helper/signal_helper.h"
namespace ad {
namespace dbcc {
namespace helper {
typedef std::pair<std::string, ad::dbcc::Signal> NameSignalPair;
typedef std::vector<NameSignalPair> NameSignalVector;
const std::string DefaultIndentSpaces = " ";
const std::string CPackAndUnpackMacros = std::string(
"#define PACK_LEFT_SHIFT(value,shift,mask) (uint8_t)((uint8_t)(value << "
"shift) & mask)\n"
"#define UNPACK_LEFT_SHIFT(_type,value,shift,mask) (_type)((_type)(value & "
"mask) << shift)\n"
"\n"
"#define PACK_RIGHT_SHIFT(value,shift,mask) (uint8_t)((uint8_t)(value >> "
"shift) & mask)\n"
"#define UNPACK_RIGHT_SHIFT(_type,value,shift,mask) (_type)((_type)(value "
"& mask) >> shift)\n");
inline void ltrim(std::string &s)
{
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
[](int ch) { return !std::isspace(ch); }));
}
inline void rtrim(std::string &s)
{
s.erase(std::find_if(s.rbegin(), s.rend(),
[](int ch) { return !std::isspace(ch); })
.base(),
s.end());
}
inline void trim(std::string &s)
{
ltrim(s);
rtrim(s);
}
inline const std::string generateIndent(int indentLevel)
{
std::stringstream ss;
for (int i = 0; i < indentLevel; i++)
{
ss << DefaultIndentSpaces;
}
return ss.str();
}
static inline int _bsf(int input)
{
int s = (int)(sizeof(input) * 8);
for (int i = 0; i < s; i++)
{
if ((input >> i) & 1)
{
s = i;
break;
}
}
return s;
}
int SignalHelper::typeLengthBsf() const
{
return _bsf(typeLength());
}
const std::string &SignalHelper::operationTypeName() const
{
static const std::string types[] =
{
"int8_t", "int16_t", "int32_t", "int64_t",
"uint8_t", "uint16_t", "uint32_t", "uint64_t"
};
if (m_signal.sign() == ad::dbcc::Sign::Signed)
{
return types[_bsf(typeLength() / 8)];
}
else
{
return types[_bsf(typeLength() / 8) + 4];
}
}
const std::string &SignalHelper::typeName() const
{
if (m_signal.isFloat())
{
static const std::string floatTypes[] = { "float", "double" };
if (m_signal.length() <= 32)
{
return floatTypes[0];
}
else
{
return floatTypes[1];
}
}
else
{
return operationTypeName();
}
}
const std::string &SignalHelper::conversionTypeSuffix() const
{
static const std::string suffixes[] =
{
"u", "u", "u", "ull"
};
return suffixes[typeLength() / 8];
}
const std::string &SignalHelper::typeSuffix() const
{
if (m_signal.isFloat())
{
static const std::string floatSuffixes[] = { "f", "" };
if (m_signal.length() == 32)
{
return floatSuffixes[0];
}
else
{
return floatSuffixes[1];
}
}
else
{
static const std::string suffixes[] =
{
"", "", "", "ll",
"u", "u", "u", "ull"
};
if (m_signal.sign() == ad::dbcc::Sign::Signed)
{
return suffixes[_bsf(typeLength() / 8)];
}
else
{
return suffixes[(typeLength() / 8) + 4];
}
}
}
std::vector<SignalHelper::Segment> SignalHelper::segments(bool invertShift) const
{
std::vector<SignalHelper::Segment> segs;
int index = m_signal.startBit() / 8;
int pos = m_signal.startBit() % 8;
int left = static_cast<int>(m_signal.length());
int length = 0;
SignalHelper::Segment seg;
while (left > 0)
{
if (m_signal.byteOrder() == ad::dbcc::ByteOrder::Motorola)
{
if (left >= (pos + 1))
{
length = (pos + 1);
pos = 7;
seg.shift = -(left - length);
seg.mask = ((1 << length) - 1);
}
else
{
length = left;
seg.shift = (pos - length + 1);
seg.mask = ((1 << length) - 1);
seg.mask <<= (pos - length + 1);
}
}
else
{
seg.shift = (left - m_signal.length()) + pos;
if (left >= (8 - pos))
{
length = (8 - pos);
seg.mask = ((1 << length) - 1);
seg.mask <<= pos;
pos = 0;
}
else
{
length = left;
seg.mask = ((1 << length) - 1);
seg.mask <<= pos;
}
}
if (invertShift)
{
if (seg.shift < 0)
{
seg.shift = -seg.shift;
seg.direction = ShiftDirection::Left;
}
else
{
seg.direction = ShiftDirection::Right;
}
}
else
{
if (seg.shift < 0)
{
seg.shift = -seg.shift;
seg.direction = ShiftDirection::Right;
}
else
{
seg.direction = ShiftDirection::Left;
}
}
seg.index = index;
segs.push_back(seg);
left -= length;
index += 1;
}
return segs;
}
std::string SignalHelper::unpack2Code(const std::string &name, const std::string &prefix, int indentLevel)
{
std::stringstream ss;
const std::string indent = generateIndent(indentLevel);
// Unpack the type information of this signal
std::string signalMemberType = typeName();
ss << indent << operationTypeName() << " " << name
<< "Temp = 0;" << std::endl;
for (auto seg : segments(true))
{
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << name << "Temp |= UNPACK_LEFT_SHIFT(";
}
else
{
ss << name << "Temp |= UNPACK_RIGHT_SHIFT(";
}
ss << operationTypeName() << ", "
<< "frame.data[" << seg.index << "], " << seg.shift << "u, "
<< std::setw(2) << std::setfill('0') << "0x" << std::hex << seg.mask
<< std::dec << "u);" << std::setw(0) << std::setfill(' ')
<< std::endl;
}
if (m_signal.sign() == ad::dbcc::Sign::Signed)
{
uint32_t mask =
((1 << (typeLength() - m_signal.length())) - 1);
if (mask != 0)
{
const std::string indent2 =
generateIndent(indentLevel + 1);
auto cts = conversionTypeSuffix();
mask <<= m_signal.length();
ss << indent << "if ((" << name << "Temp & (1"
<< cts << " << " << (m_signal.length() - 1)
<< ")) != 0" << cts << ")" << std::endl
<< indent << "{" << std::endl
<< indent2 << name << "Temp |= 0x" << std::hex
<< mask << std::dec << cts << ";" << std::endl
<< indent << "}" << std::endl;
}
}
ss << indent << prefix << name << " = ("
<< signalMemberType << ")(" << name << "Temp * "
<< m_signal.factor() << " + (" << m_signal.offset() << ")"
<< ");";
return ss.str();
}
std::string SignalHelper::pack2Code(const std::string &name, const std::string &prefix, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = generateIndent(indentLevel);
ss << indent << operationTypeName() << " " << name
<< "Temp = (" << operationTypeName() << ")((" << prefix
<< name << " - (" << m_signal.offset() << ")) / "
<< m_signal.factor() << ");" << std::endl;
for (auto seg : segments())
{
if (needNewLine)
{
ss << std::endl;
}
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_LEFT_SHIFT(";
}
else
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_RIGHT_SHIFT(";
}
ss << name << "Temp, " << seg.shift << "u, " << std::setw(2)
<< std::setfill('0') << "0x" << std::hex << seg.mask << std::dec
<< "u);" << std::setw(0) << std::setfill(' ');
needNewLine = true;
}
return ss.str();
}
} // namespace helper
} // namespace dbcc
} // namespace ad

67
dbcc/source/message.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "dbcc/message.h"
#include <iostream>
#include <limits>
#include <algorithm>
namespace ad {
namespace dbcc {
std::istream &operator>>(std::istream &in, Message &msg)
{
std::string preamble;
in >> preamble;
// Check if we are actually reading a message otherwise fail the stream
if (preamble != "BO_")
{
in.setstate(std::ios_base::failbit);
return in;
}
// Parse the message ID.
in >> msg.m_id;
// Parse the name of the message
std::string name;
in >> name;
msg.m_name = name.substr(0, name.length() - 1);
// Parse the message length
in >> msg.m_dlc;
// Parse the sender
in >> msg.m_from;
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// As long as there is a signal, parse the signal
while (in)
{
Signal sig;
in >> sig;
if (in)
{
msg.appendSignal(sig);
}
}
in.clear();
return in;
}
std::set<std::string> Message::to() const
{
std::set<std::string> collection;
for (auto sig : m_signals)
{
auto toList = sig.to();
collection.insert(toList.begin(), toList.end());
}
return collection;
}
} // namespace dbcc
} // namespace ad

589
dbcc/source/signal.cpp Normal file
View File

@ -0,0 +1,589 @@
#include "dbcc/signal.h"
#include <sstream>
#include <limits>
#include <iterator>
#include <vector> /**< std::vector */
#include <algorithm>
#include "dbcc/helper/signal_helper.h"
// FIXME: We ignore the properties and comment of Signal
namespace ad {
namespace dbcc {
static inline std::string &trim(std::string &str, const std::string &toTrim = " ")
{
std::string::size_type pos = str.find_last_not_of(toTrim);
if (pos == std::string::npos)
{
str.clear();
}
else
{
str.erase(pos + 1);
str.erase(0, str.find_first_not_of(toTrim));
}
return str;
}
static inline std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems)
{
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
static inline std::vector<std::string> split(const std::string &s, char delim)
{
std::vector<std::string> elems;
split(s, delim, elems);
return elems;
}
static inline bool consume_number(std::istream &in, double &num)
{
int64_t temp = 0;
in >> temp;
char c = in.peek();
if (c == '.')
{
double fraction = 0;
in.ignore(1); // .
in >> fraction;
num = temp + fraction;
return true;
}
num = temp;
return false;
}
std::istream &operator>>(std::istream &in, Signal &sig)
{
std::string preamble;
in >> preamble;
// Check if we are actually reading a signal otherwise fail the stream
if (preamble != "SG_")
{
in.setstate(std::ios_base::failbit);
return in;
}
sig.m_isFloat = false;
// Parse the signal name
in >> sig.m_name;
std::string multi;
in >> multi;
// This case happens if there is not Multiplexor present
if (multi == ":")
{
sig.m_multiplexor = Multiplexor::None;
}
else
{
if (multi == "M")
{
sig.m_multiplexor = Multiplexor::Multiplexor;
}
else
{
// The multiplexor looks like that 'm12' so we ignore the m and parse it as integer
std::istringstream multistream(multi);
multistream.ignore(1);
uint16_t multiNum;
multistream >> multiNum;
sig.m_multiplexor = Multiplexor::Multiplexor;
sig.m_multiplexedNumber = multiNum;
}
// Ignore the next character which is a ':'
in >> multi;
}
in >> sig.m_startBit;
in.ignore(1);
in >> sig.m_length;
in.ignore(1);
int order;
in >> order;
if (order == 0)
{
sig.m_byteOrder = ByteOrder::Motorola;
}
else
{
sig.m_byteOrder = ByteOrder::Intel;
}
char sign;
in >> sign;
if (sign == '+')
{
sig.m_sign = Sign::Unsigned;
}
else
{
sig.m_sign = Sign::Signed;
}
bool isfloat = false;
// (factor,offset) [max|min]
in.ignore(std::numeric_limits<std::streamsize>::max(), '(');
isfloat = consume_number(in, sig.m_factor) || isfloat;
in.ignore(1); // ,
isfloat = consume_number(in, sig.m_offset) || isfloat;
in.ignore(1); // )
in.ignore(std::numeric_limits<std::streamsize>::max(), '[');
isfloat = consume_number(in, sig.m_minimum) || isfloat;
in.ignore(1); // |
isfloat = consume_number(in, sig.m_maximum) || isfloat;
in.ignore(1); // ]
sig.m_isFloat = isfloat;
// Unit string
std::stringstream unit;
in.ignore(std::numeric_limits<std::streamsize>::max(), '\"');
while (in.peek() == '\"')
{
unit.put(in.get());
}
if (in.eof() || !in)
{
in.setstate(std::ios_base::failbit);
return in;
}
std::string unitstr = unit.str();
sig.m_unit = trim(unitstr, "\"");
std::string to;
getline(in, to);
if (!to.empty() && *to.rbegin() == '\r')
{
to.erase(to.length() - 1, 1);
}
if (!to.empty())
{
std::vector<std::string> toStrings = split(to, ',');
std::move(toStrings.begin(), toStrings.end(), std::inserter(sig.m_to, sig.m_to.begin()));
}
return in;
}
Signal::~Signal()
{
}
bool Signal::compile()
{
ad::dbcc::helper::SignalHelper sigHelper(*this);
for (auto seg : sigHelper.segments(true))
{
CompiledSignalParameter param;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
param.left = 1;
}
else
{
param.left = 0;
}
param.index = seg.index;
param.shift = seg.shift;
param.mask = seg.mask;
m_params.emplace_back(param);
}
if (sign() == ad::dbcc::Sign::Signed)
{
uint32_t mask =
((1 << (sigHelper.typeLength() - length())) - 1);
if (mask != 0)
{
mask <<= length();
m_signedLengthMask = mask;
}
}
#if defined(DEBUG) && defined(VERBOSE)
std::cout << *this << std::endl;
#endif /* DEBUG && VERBOSE */
return true;
}
static uint8_t _packLeftShift(uint64_t value, uint32_t shift, uint32_t mask)
{
return (uint8_t)((uint8_t)(value << shift) & mask);
}
static uint8_t _packRightShift(uint64_t value, uint32_t shift, uint32_t mask)
{
return (uint8_t)((uint8_t)(value >> shift) & mask);
}
static int64_t _unpackLeftShift(uint8_t value, uint32_t shift, uint32_t mask)
{
return static_cast<int64_t>(static_cast<int64_t>(value & mask) << shift);
}
static int64_t _unpackRightShift(uint8_t value, uint32_t shift, uint32_t mask)
{
return static_cast<int64_t>(static_cast<int64_t>(value & mask) >> shift);
}
bool Signal::encode(const ParsedValue &pv, uint8_t *data, size_t /* length */)
{
if (m_params.size() == 0)
{
compile();
}
if (pv.isInteger != isFloat())
{
return false;
}
int64_t temp = 0;
if (pv.isInteger)
{
temp = static_cast<int64_t>((pv.i - offset()) / factor());
}
else
{
temp = static_cast<int64_t>((pv.f - offset()) / factor());
}
for (auto &p : m_params)
{
if (p.left)
{
data[p.index] |= _packRightShift(temp, p.shift, p.mask);
}
else
{
data[p.index] |= _packLeftShift(temp, p.shift, p.mask);
}
}
return true;
}
bool Signal::decode(const uint8_t *data, size_t /* length */, ParsedValue &pv)
{
if (m_params.size() == 0)
{
compile();
}
int64_t result = 0;
pv.isInteger = !isFloat();
for (auto &p : m_params)
{
if (p.left)
{
result |= _unpackLeftShift(data[p.index], p.shift, p.mask);
}
else
{
result |= _unpackRightShift(data[p.index], p.shift, p.mask);
}
}
if (m_signedLengthMask != 0)
{
if ((result & (static_cast<int64_t>(1) << (length() - 1))) != 0)
{
result |= m_signedLengthMask;
}
}
if (pv.isInteger)
{
pv.i = static_cast<int32_t>(result * factor() + offset());
}
else
{
pv.f = static_cast<float>(result * factor() + offset());
}
return true;
}
bool Signal::encode(double value, uint8_t *data, size_t /* length */)
{
if (m_params.size() == 0)
{
compile();
}
int64_t temp = 0;
if (!isFloat())
{
int32_t i = static_cast<int32_t>(value);
temp = static_cast<int64_t>((i - offset()) / factor());
}
else
{
temp = static_cast<int64_t>((value - offset()) / factor());
}
for (auto &p : m_params)
{
if (p.left)
{
data[p.index] |= _packRightShift(temp, p.shift, p.mask);
}
else
{
data[p.index] |= _packLeftShift(temp, p.shift, p.mask);
}
}
return true;
}
bool Signal::decode(const uint8_t *data, size_t /* length */, double &value)
{
if (m_params.size() == 0)
{
compile();
}
int64_t result = 0;
for (auto &p : m_params)
{
if (p.left)
{
result |= _unpackLeftShift(data[p.index], p.shift, p.mask);
}
else
{
result |= _unpackRightShift(data[p.index], p.shift, p.mask);
}
}
if (m_signedLengthMask != 0)
{
if ((result & (static_cast<int64_t>(1) << (length() - 1))) != 0)
{
result |= m_signedLengthMask;
}
}
if (!isFloat())
{
value = static_cast<double>(static_cast<int32_t>(result * factor() + offset()));
}
else
{
value = static_cast<double>(result * factor() + offset());
}
return true;
}
// Here I assume the layout:
//
// Bit
//
// 7 6 5 4 3 2 1 0
// +---+---+---+---+---+---+---+---+
// 0 |<-x|<---------------------x|<--|
// +---+---+---+---+---+---+---+---+
// 1 |-------------------------------|
// +---+---+---+---+---+---+---+---+
// 2 |----------x| | | | | |
// B +---+---+---+---+---+---+---+---+
// y 3 | | | | | | | | |
// t +---+---+---+---+---+---+---+---+
// e 4 | | | | | | | | |
// +---+---+---+---+---+---+---+---+
// 5 | | | | | | | | |
// +---+---+---+---+---+---+---+---+
// 6 | | | | | | | | |
// +---+---+---+---+---+---+---+---+
// 7 | | | | | | | | |
// +---+---+---+---+---+---+---+---+
//
// Reference: https://github.com/cantools/cantools#the-dump-subcommand
bool Signal::bitsLayout(LayoutInfo &info)
{
info.bits.clear();
info.byteLines.clear();
info.byteLineRange.clear();
if (m_byteOrder == ByteOrder::Intel) //< little-endian
{
int lineStart = m_startBit / 8;
int remainder = 8 - m_startBit % 8;
int lineNum = ((m_length - remainder + 7) / 8) + 1;
int startBit = m_startBit;
int endBit = startBit;
if (remainder > m_length)
{
endBit += m_length;
}
else
{
endBit += remainder;
}
remainder = m_length;
for (int line = lineStart; line < lineStart + lineNum; ++line)
{
for (int bitIdx = startBit; bitIdx < endBit; bitIdx++)
{
info.bits.push_back(bitIdx);
}
info.byteLines.push_back(line);
info.byteLineRange.emplace_back(std::pair<int, int>{(9 - startBit % 8) - 1, (8 - (endBit - 1) % 8) - 1});
remainder = remainder - (startBit - endBit);
startBit = 8 * (line + 1);
if (remainder >= 8)
{
endBit = startBit + 8;
}
else
{
endBit = startBit + remainder;
}
}
}
else //< big-endian
{
int lineStart = m_startBit / 8;
int remainder = (8 - (8 - (m_startBit + 1) % 8) % 8);
int lineNum = ((m_length - remainder + 7) / 8) + 1;
int startBit = m_startBit;
int lineRemainder = (startBit % 8) + 1;
int endBit = startBit;
if (lineRemainder > m_length)
{
endBit -= m_length;
}
else
{
endBit -= lineRemainder;
}
remainder = m_length;
for (int line = lineStart; line < lineStart + lineNum; ++line)
{
for (int bitIdx = startBit; bitIdx > endBit; bitIdx--)
{
info.bits.push_back(bitIdx);
}
info.byteLines.push_back(line);
info.byteLineRange.emplace_back(std::pair<int, int>{(9 - (endBit + 1) % 8) - 1, (8 - startBit % 8) - 1});
remainder = remainder - (startBit - endBit);
startBit = 8 * (line + 2) - 1;
if (remainder >= 8)
{
endBit = startBit - 8;
}
else
{
endBit = startBit - remainder;
}
}
}
return true;
}
std::ostream &operator<<(std::ostream &out, Signal::CompiledSignalParameter &p)
{
out << "CompiledSignalParameter: { index: " << p.index << ", "
<< "shift: " << p.shift << ", "
<< "mask: 0x" << std::hex << p.mask << std::dec << ", "
<< "dir: " << (p.left == 1 ? "Left" : "Right")
<< " }";
return out;
}
std::ostream &operator<<(std::ostream &out, Signal::LayoutInfo &p)
{
out << "Bits: { ";
for (auto bit : p.bits)
{
out << bit << ", ";
}
out << " }\n";
for (size_t idx = 0; idx < p.byteLines.size(); idx++)
{
out << "Line(" << p.byteLines[idx] << "): [" << p.byteLineRange[idx].first << ", " << p.byteLineRange[idx].second << "]\n";
}
return out;
}
std::ostream &operator<<(std::ostream &out, Signal &cs)
{
int i = 0;
out << "Message: " << cs.getMessageId() << ", Signal: " << cs.name()
<< (cs.isFloat() ? ", float" : "") << std::endl;
for (auto &p : cs.m_params)
{
out << " param" << i << ": " << p << std::endl;
i++;
}
if (cs.m_signedLengthMask != 0)
{
out << " mask: 0x" << std::hex << cs.m_signedLengthMask << std::dec << std::endl;
}
out << " factor: " << cs.factor() << ", offset: " << cs.offset();
return out;
}
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,227 @@
#include "dbcc/signal_processor.h"
#include <tuple>
namespace ad {
namespace dbcc {
size_t SignalProcessor::_signalIndex(uint32_t msgId, const std::string &signalName)
{
auto msgIter = m_dbcIter.find(msgId);
if (msgIter == m_dbcIter.end())
{
return InvalidIndex;
}
if (!msgIter->contains(signalName))
{
return InvalidIndex;
}
return (*msgIter)[signalName];
}
bool SignalProcessor::decodeMessage(uint32_t msgId, const uint8_t *data, size_t length)
{
ad::dbcc::ParsedValue pv;
auto iter = m_decoderMap.find(msgId);
if (iter == m_decoderMap.end())
{
return false;
}
ad::dbcc::Message msg;
std::vector<size_t> vt;
std::tie(msg, vt) = iter->second;
for (size_t i = 0; i < vt.size(); i++)
{
auto & sig = msg[vt[i]];
if (sig.decode(data, length, pv))
{
sig.getDecodeDelegate()->onDecoded(msgId, sig.name(), pv);
}
else
{
std::cout << "[WARNING] Failed to decode: " << msgId << ":" << sig.name() << std::endl;
return false;
}
}
return true;
}
bool SignalProcessor::encodeMessage(uint32_t msgId, const SignalValues &pvs, uint8_t *data, size_t length)
{
auto iter = m_encoderMap.find(msgId);
if (iter == m_encoderMap.end())
{
return false;
}
ad::dbcc::Message msg;
std::vector<size_t> vt;
std::tie(msg, vt) = iter->second;
for (size_t i = 0; i < pvs.size(); i++)
{
auto &pv = pvs[i];
auto idx = msg[pv.first];
if (idx != InvalidIndex)
{
auto &sig = msg[idx];
if (sig.encode(pv.second, data, length))
{
if (!sig.getEncodeDelegate()->onEncoded(msgId, sig.name(), data, length, pv.second, i == pvs.size() - 1))
{
break;
}
}
else
{
std::cout << "[WARNING] Failed to encode: " << msgId << ":" << sig.name() << std::endl;
return false;
}
}
else
{
std::cout << "[WARNING] The signal (" << msgId << ":" << pv.first << ") does not exist!" << std::endl;
return false;
}
}
return true;
}
bool SignalProcessor::encodeMessage(uint32_t msgId, const SignalValuesVector &pvs, uint8_t *data, size_t length)
{
auto iter = m_encoderMap.find(msgId);
if (iter == m_encoderMap.end())
{
return false;
}
ParsedValue rpv;
ad::dbcc::Message msg;
std::vector<size_t> vt;
std::tie(msg, vt) = iter->second;
for (size_t i = 0; i < pvs.size(); i++)
{
auto &pv = pvs[i];
auto idx = msg[pv.first];
if (idx != InvalidIndex)
{
auto &sig = msg[idx];
if (sig.encode(pv.second, data, length))
{
rpv.isInteger = !sig.isFloat();
if (rpv.isInteger)
{
rpv.i = static_cast<int32_t>(pv.second);
}
else
{
rpv.f = static_cast<float>(pv.second);
}
if (!sig.getEncodeDelegate()->onEncoded(msgId, sig.name(), data, length, rpv, i == pvs.size() - 1))
{
break;
}
}
else
{
std::cout << "[WARNING] Failed to encode: " << msgId << ":" << sig.name() << std::endl;
return false;
}
}
else
{
std::cout << "[WARNING] The signal (" << msgId << ":" << pv.first << ") does not exist!" << std::endl;
return false;
}
}
return true;
}
bool SignalProcessor::encodeMessage(uint32_t msgId, const SignalValuesMap &pvs, uint8_t *data, size_t length)
{
auto iter = m_encoderMap.find(msgId);
if (iter == m_encoderMap.end())
{
return false;
}
ParsedValue rpv;
ad::dbcc::Message msg;
std::vector<size_t> vt;
std::tie(msg, vt) = iter->second;
size_t i = 0;
for (auto &kv : pvs)
{
auto idx = msg[kv.first];
if (idx != InvalidIndex)
{
auto &sig = msg[idx];
if (sig.encode(kv.second, data, length))
{
rpv.isInteger = !sig.isFloat();
if (rpv.isInteger)
{
rpv.i = static_cast<int32_t>(kv.second);
}
else
{
rpv.f = static_cast<float>(kv.second);
}
if (!sig.getEncodeDelegate()->onEncoded(msgId, sig.name(), data, length, rpv, i == pvs.size() - 1))
{
break;
}
}
else
{
std::cout << "[WARNING] Failed to encode: " << msgId << ":" << sig.name() << std::endl;
return false;
}
}
else
{
std::cout << "[WARNING] The signal (" << msgId << ":" << kv.first << ") does not exist!" << std::endl;
return false;
}
++i;
}
return true;
}
} // namespace dbcc
} // namespace ad

25
dbcc/source/version.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "dbcc/version.h"
#define MACRO_STRINGIFY(macro_or_string) MACRO_STRINGIFY_ARG(macro_or_string)
#define MACRO_STRINGIFY_ARG(contents) #contents
#define DBCC_VERSION_MAJOR 0
#define DBCC_VERSION_MINOR 1
#define DBCC_VERSION_PATCH 1
namespace ad {
namespace dbcc {
std::string version()
{
static const std::string VersionString = MACRO_STRINGIFY(DBCC_VERSION_MAJOR) "." MACRO_STRINGIFY(DBCC_VERSION_MINOR) "." MACRO_STRINGIFY(DBCC_VERSION_PATCH);
#if defined(DEBUG)
return VersionString + " Debug";
#else /* !DEBUG */
return VersionString;
#endif /* DEBUG */
}
} // namespace dbcc
} // namespace ad

62
dbcc/test/main.cpp Normal file
View File

@ -0,0 +1,62 @@
#include <iostream> /**< std::cout std::cerr std::endl */
#include <string> /**< std::string */
#include <memory> /**< std::shared_ptr */
#include "dbcc/signal_delegate.h"
#include "dbcc/dbc_iterator.h"
#include "dbcc/signal_processor.h"
const std::string usage = "This parser is meant to be used via CLi at the moment\n"
"\t./parser <FILE>\n";
class MyDelegate : public ad::dbcc::SignalDecodeDelegate
{
virtual void onDecoded(uint32_t msgId, const std::string &signalName, double pv, void */* userData */)
{
std::cout << "msg: #0x" << std::hex << msgId << std::dec << ", " << signalName << " -> pv: " << pv << std::endl;
}
};
int main(int argc, char *argv[])
{
if (argc < 2)
{
std::cout << usage << std::endl;
return 0;
}
ad::dbcc::DbcIterator iter(argv[1]);
for (auto msg : iter)
{
std::cout << "msg: " << msg.name() << std::endl;
}
std::shared_ptr<ad::dbcc::SignalDecodeDelegate> delegate = std::make_shared<MyDelegate>();
uint8_t data[8] = { 0 };
data[0] = 0x01;
data[1] = 0xA8;
uint8_t data1[2] = { 0x40, 0x0 };
ad::dbcc::SignalProcessor sp(iter);
sp.registerDelegate(578, "ACCAccReqValHSC2", delegate);
sp.registerDelegate(498, "SysBPMHSC2", delegate);
sp.registerDelegate(498, "SysBPMEnbdHSC2", delegate);
sp.decodeMessage(578, data, 8);
sp.decodeMessage(498, data1, 2);
std::cout << "Keep the order stable" << std::endl;
for (auto msg : iter)
{
std::cout << "msg: " << msg.name() << std::endl;
}
// Unpacked value: -5.1
return 0;
}

37
dbcc/test/test.py Normal file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import dbcc
class MyDelegate(dbcc.SignalDelegate):
def __init__(self):
super().__init__()
def on_encoded(self, msg_id: int, db: bytearray):
print("msg:", msg_id, ":", db)
def on_decoded(self, msg_id: int, signal_name: str, value: float):
print("msg:", msg_id, ",", signal_name, ":", value)
if __name__ == '__main__':
myhandler = MyDelegate()
msg_id = 578
sp = dbcc.SignalProcessor(sys.argv[1])
print(sp.register_delegate(msg_id, "ACCAccReqValHSC2", myhandler))
print(sp.register_delegate(498, "SysBPMHSC2", myhandler))
print(sp.register_delegate(498, "SysBPMEnbdHSC2", myhandler))
data = bytearray([0x01, 0xA8, 0, 0, 0, 0, 0, 0])
sp.decode_message(msg_id, data)
sp.encode_message(msg_id, {"ACCAccReqValHSC2": -5.1})
data = bytearray([0x40, 0x0])
sp.decode_message(498, data)
sp.encode_message(498, {"SysBPMHSC2": 2, "SysBPMEnbdHSC2": 1})

View File

@ -0,0 +1,152 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterNamespace: false
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: None
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^stdafx\.h$'
Priority: 0
- Regex: '^<ext/.*\.h>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseTab: Never
...

4
dbcc/tools/blf/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.build/
lib/
bin/
compile_commands.json

View File

@ -0,0 +1,71 @@
cmake_minimum_required (VERSION 3.0)
# Always use lower case for project name
project (blf)
# For IDEs
# When using IDE to open this cmake project, it will never to append the
# argument `CMAKE_MODULE_PATH`, so we must set it in the cmake file
# before including AD cmake scripts.
# Please replace the follow argument with the real path in your system,
# make sure it points to the cmake directory in tools.
if (NOT DEFINED CMAKE_MODULE_PATH)
set (CMAKE_MODULE_PATH "${HOME}/.AD/.cmake")
endif ()
# Include the common configurations
include (admake)
# AdMake provides some prebuilt modules you can find them
# in AD/3rd-party in AD Group
# NOTE:
# All the modules will export an environment variable
# to indicate the corresponding libraries and its dependencies,
# the variable named as <MODULE-NAME>_LIB, the '-' in module name will be replaced with '_'
# for example, zlib -> ZLIB_LIB, sqlite-zipvfs -> SQLITE_ZIPVFS_LIB
# By default, the 3rd modules will always use release libraries and
# modules will always use the same configuration of current project.
#
# append_3rd_modules (zlib sqlite-zipvfs)
# append_modules (base msgsdk)
# If you build the test project with admake(append the -t option)
# admake will pass the parameter ADMAKE_BUILD_TEST
if (NOT DEFINED ADMAKE_BUILD_TEST)
set (TARGET_NAME ${CMAKE_PROJECT_NAME})
# Include other packages, such as the following
# find_package (MySQL REQUIRED)
# includd_directories (${MySQL_DIRECTORIES})
include_directories (
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/source")
file (GLOB_RECURSE SRC "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
# Other 3rd-party modules only used by the main project
# append_3rd_modules ("cpputil")
# add_executable (${TARGET_NAME} ${SRC})
# add_library (${TARGET_NAME} SHARED ${SRC})
add_library (${TARGET_NAME} STATIC ${SRC})
# Build the dependencies automatically, IT IS VERY USEFUL
# add_dependencies (${TARGET_NAME} ${BASE_DEPS} ${MSGSDK_DEPS} ${ADBUS_DEPS})
# target_link_libraries (${TARGET_NAME} ${SQLITE_ZIPVFS_LIB} ${ZLIB_LIB} ${CPPUTIL_LIB} ${MYSQL_LIBRARY} ${DEPENDENCE_LIB})
else ()
set (TARGET_NAME_TEST ${CMAKE_PROJECT_NAME}_test)
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/test")
# If the main project is a library
# append_moudles (${CMAKE_PROJECT_NAME})
append_3rd_modules (gtest)
file (GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/test/*.cpp")
add_executable (${TARGET_NAME_TEST} ${SRC})
target_link_libraries (${TARGET_NAME_TEST} ${GTEST_LIB} ${DEPENDENCE_LIB})
endif ()

10
dbcc/tools/blf/README.md Normal file
View File

@ -0,0 +1,10 @@
# Blf
## Features
## How to Build
## How to Run
## TODO

View File

@ -0,0 +1,13 @@
# Append dependencies of this module
# append_modules (util base)
# append_3rd_modules (rapidjson)
# Overwrite the default variables: ${MODULE_UPPER}_DEPS, ${modules}_deps ${MODULE_UPPER}_LIB
# are all the convention variables, for more details, please see the document of adtools.
set (${MODULE_UPPER}_DEPS "${module}_deps;${UTIL_DEPS};${BASE_DEPS}" CACHE INTERNAL "${MODULE_UPPER_LIB}")
# The variable ${MODULE_UPPER}_LIB should always contains it's dependencies,
# so that the libraries which referred this module should only contains ${MODULE_UPPER}_LIB
# and never need to care about the other indirect dependencies.
set (_REQUIRED_LIBS "${module_lib_name};${UTIL_LIB};${BASE_LIB}")
set (${MODULE_UPPER}_LIB "${_REQUIRED_LIBS}" CACHE INTERNAL "${MODULE_UPPER}_LIB")

View File

@ -0,0 +1,201 @@
#pragma once
#include <cstring> /**< memset */
#include <cstdint> /**< uint8_t uint16_t uint32_t uint64_t */
#include <string> /**< std::string */
#include <fstream> /**< std::fstream */
namespace ad {
namespace dbcc {
const uint32_t BlfFileMagic = 0x01234567;
const uint32_t BlfObjectSignature = 0x4A424F4C; /**< 'LOBJ' */
const uint32_t BlfLogSignature = 0x47474F4C; /**< 'LOGG' */
enum BinaryLogObjType
{
CanMessage = 1,
CanStatistic = 4,
LogContainer = 10,
CanDriverError = 31,
CanErrorExt = 73,
CanMessage2 = 86
}
enum BinaryLogObjFlag
{
TimeTenMics = 1,
TimeOneNans = 2
}
#pragma pack(push, 1)
struct SystemTime
{
uint16_t year;
uint16_t month;
uint16_t dayOfWeek;
uint16_t day;
uint16_t hour;
uint16_t minute;
uint16_t second;
uint16_t milliseconds;
SystemTime()
{
year = 0;
month = 0;
dayOfWeek = 0;
day = 0;
hour = 0;
minute = 0;
second = 0;
milliseconds = 0;
}
};
struct LogEntity
{
uint32_t signature;
uint32_t headerSize;
uint32_t crc;
uint8_t appId;
uint8_t compression;
uint8_t appMajor;
uint8_t appMinor;
uint64_t fileSize;
uint64_t uncompressedFileSize;
uint32_t objectCount;
uint8_t appBuild;
uint8_t reserved1;
uint8_t reserved2;
uint8_t reserved3;
SystemTime measurementStartTime;
SystemTime measurementEndTime;
uint8_t reserved4[72];
LogEntity()
{
signature = 0;
headerSize = 0;
crc = 0;
appId = 0;
compression = 0;
appMajor = 0;
appMinor = 0;
fileSize = 0;
uncompressedFileSize = 0;
objectCount = 0;
appBuild = 0;
reserved1 = 0;
reserved2 = 0;
reserved3 = 0;
memset(reserved4, 0, sizeof(reserved4);
}
};
struct VblObjectHeaderBase /**< 16 */
{
uint32_t signature; /**< 0: "LOBJ" SIGNATURE */
uint16_t headerSize; /**< 4: header size */
uint16_t headerVersion; /**< 6: 1=VBLObjectHeader, 2=VBLObjectHeader2*/
uint32_t objectSize; /**< 8: object size*/
uint32_t objectType; /**< 12: block type */
};
struct VblObjectHeader /**< 32 */
{
VblObjectHeaderBase base;
uint32_t objectFlags; /**< 16 */
uint16_t reserved; /**< 20 */
uint16_t objectVersion; /**< 22 */
uint64_t objectTimeStamp; /**< 24..31 */
};
struct VblObjectHeaderBaseLogEntity /**< 32 */
{
VblObjectHeaderBase base; /**< 0: base */
uint32_t compressedflag; /**< 16: compressed data=2 */
uint32_t reserved1; /**< 20: reserved */
uint32_t deflatebuffersize; /**< 24: uncompressed size*/
uint32_t reserved2; /**< 28: reserved */
};
struct VblCanMessage
{
VblObjectHeader header; /**< 0: header */
uint16_t channel; /**< 32: channel*/
uint8_t flags; /**< 34: flags */
uint8_t dlc; /**< 35: DLC */
uint32_t id; /**< 36: message ID*/
uint8_t data[8]; /**< 40 */
};
#pragma pack(pop)
struct VblFileStatistics
{
uint32_t mStatisticsSize; /* sizeof (VBLFileStatistics) */
uint8_t mApplicationID; /* application ID */
uint8_t mApplicationMajor; /* application major number */
uint8_t mApplicationMinor; /* application minor number */
uint8_t mApplicationBuild; /* application build number */
uint64_t mFileSize; /* file size in bytes */
uint64_t mUncompressedFileSize; /* uncompressed file size in bytes */
uint32_t mObjectCount; /* number of objects */
uint32_t mObVblFileStatisticsjectsRead; /* number of objects read */
};
struct VblFileStatisticsEx
{
uint32_t mStatisticsSize; /* sizeof (VBLFileStatisticsEx) */
uint8_t mApplicationID; /* application ID */
uint8_t mApplicationMajor; /* application major number */
uint8_t mApplicationMinor; /* application minor number */
uint8_t mApplicationBuild; /* application build number */
uint64_t mFileSize; /* file size in bytes */
uint64_t mUncompressedFileSize; /* uncompressed file size in bytes */
uint32_t mObjectCount; /* number of objects */
uint32_t mObjectsRead; /* number of objects read */
SystemTime mMeasurementStartTime; /* measurement start time */
SystemTime mLastObjectTime; /* last object time */
uint32_t mReserved[18]; /* reserved */
VblFileStatisticsEx()
{
s->mStatisticsSize = 0x88u;
s->mApplicationID = 0;
s->mApplicationMajor = 0;
s->mApplicationMinor = 0;
s->mApplicationBuild = 0;
s->mFileSize = 0;
s->mUncompressedFileSize = 0;
s->mObjectCount = 0;
s->mObjectsRead = 0;
blfSystemTimeInit(&s->mMeasurementStartTime);
blfSystemTimeInit(&s->mLastObjectTime);
assert(sizeof(s->mReserved) == 0x48);
blfMemZero((uint8_t *)(s->mReserved),
sizeof(s->mReserved));
}
};
struct BlfHandle
{
uint32_t magic;
LogEntity mLOGG;
DualStream mDualStream;
uint32_t mPeekFlag;
VBLFileStatisticsEx mStatistics;
uint32_t mCANMessageFormat_v1;
};
class BlfStream : public std::fstream
{
public:
BlfStream(const char *filepath, std::ios_base::openmode mode = ios_base::in | ios_base::out);
BlfStream(const std::string &filepath, std::ios_base::openmode mode = ios_base::in | ios_base::out);
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,8 @@
#include <iostream> /**< std::cout std::endl */
int main(int /* argc */, char * /* argv */[])
{
std::cout << "blf" << std::endl;
return 0;
}

View File

@ -0,0 +1,11 @@
#include "gtest.h"
int main(int argc, char **argv)
{
int result = 0;
::testing::InitGoogleTest(&argc, argv);
result = RUN_ALL_TESTS();
return result;
}

View File

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.15)
project (dbcc_gen)
include (admake)
set (TARGET_NAME ${CMAKE_PROJECT_NAME}${SUFFIX_STR})
append_modules (dbcc)
append_3rd_modules (rapidjson)
include_directories (
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/source")
file (GLOB_RECURSE SRC
"${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
add_executable (${TARGET_NAME} ${SRC})
add_dependencies (${TARGET_NAME} ${DBCC_DEPS})
target_link_libraries (${TARGET_NAME} ${DBCC_LIB})
set_property (TARGET ${TARGET_NAME}
PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)

View File

@ -0,0 +1,36 @@
#pragma once
#include <string> /**< std::string */
namespace ad {
namespace dbcc {
class Generator
{
public:
enum Language
{
C,
CPlusPlus,
CSharp,
Rust,
Java,
Js
};
public:
Generator(const std::string &dbc);
Generator(const std::string &dbc, const std::string &config);
~Generator();
bool generate(const std::string &path = "",
Language lang = Language::CPlusPlus);
private:
std::string m_dbcFile;
std::string m_configFile;
Language m_language;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,2 @@
--index-url https://opentuna.cn/pypi/web/simple
cantools

View File

@ -0,0 +1,30 @@
# Generate C source code frm the DBC file via cantools
#
# NOTE: Please make sure the code page of your PowerShell is UTF-8
#
# Author: donkey <anjingyu_ws@foxmail.com>
$PythonExe = "python"
$MajorVersion = $(& $PythonExe -V)
$MajorVersion = $($(-Split "$MajorVersion" | Select -Index 1) -Split "\." | Select -First 1)
if ("$MajorVersion" -ne "3") {
if (Get-Command "python3" -ErrorAction SilentlyContinue) {
$PythonExe = "python3"
} else {
echo "Please install python3 first."
exit -1
}
}
# Check required package
& $PythonExe -c "import cantools" >$null 2>&1
if ("$LastExitCode" -eq "0") {
& $PythonExe -m cantools generate_c_source --database-name can_data $args
} else {
echo "Please install cantools: ${PythonExe} -m pip install -U cantools"
exit -2
}

View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
#
# Generate DBC C source code via cantools
#
# Author: donkey <anjingyu_ws@foxmail.com>
readonly OSTYPE=$(uname -s)
# Check whether the default python is Python3
PYTHON_EXECUTABLE="python"
if [ "$OSTYPE" = "Linux" ] || [ "$OSTYPE" = "Drawin" ]; then
MAJOR_VERSION=`$PYTHON_EXECUTABLE --version 2>&1 >/dev/null`
else # CYGWIN_NT* MINGW64_NT*, on Windows
MAJOR_VERSION=`$PYTHON_EXECUTABLE --version`
fi
MAJOR_VERSION=$(echo "${MAJOR_VERSION}" | cut -d" " -f 2 | cut -d"." -f 1)
if [ "${MAJOR_VERSION}" != "3" ]; then
if command -v "python3" 1>/dev/null 2>&1; then
PYTHON_EXECUTABLE="python3"
else
echo "Please install python3 first."
exit -1
fi
fi
# Check required package
$PYTHON_EXECUTABLE -c "import cantools" 1>/dev/null 2>&1
if [ "$?" = "0" ]; then
$PYTHON_EXECUTABLE -m cantools generate_c_source --database-name can_data "$@"
else
echo "Please install cantools: ${PYTHON_EXECUTABLE} -m pip install -U cantools"
exit -2
fi

View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
# -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8
#
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# Version 2, December 2004
#
# Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
#
# Everyone is permitted to copy and distribute verbatim or modified
# copies of this license document, and changing it is allowed as long
# as the name is changed.
#
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
#
# 0. You just DO WHAT THE FUCK YOU WANT TO.
#
# Copyright (c) 2021- donkey <anjingyu_ws@foxmail.com>
import sys
import json
import cantools
from pprint import pprint
__author__ = ['"donkey" <anjingyu_ws@foxmail.com>']
if __name__ == '__main__':
db = cantools.database.load_file(sys.argv[1], strict=True)
for msg in db.messages:
pprint(msg.signals)

View File

@ -0,0 +1,523 @@
#include "c_generator.h"
#include <iostream> /**< std::cout std::cerr std::endl std::hex std::dec */
#include <iomanip> /**< std::setw std::setfill */
#include <fstream> /**< std::ifstream */
namespace ad {
namespace dbcc {
static std::string _signalUnpack2Code(ad::dbcc::NameSignalPair &sig,
int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
// Unpack the type information of this signal
std::string signalMemberName = sig.first;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = sig.first.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = sig.first.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = sig.first.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << indent << sigHelper.operationTypeName() << " " << signalMemberName
<< "Temp = 0;" << std::endl;
for (auto seg : sigHelper.segments(true))
{
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << signalMemberName << "Temp |= UNPACK_LEFT_SHIFT(";
}
else
{
ss << signalMemberName << "Temp |= UNPACK_RIGHT_SHIFT(";
}
ss << sigHelper.operationTypeName() << ", "
<< "frame->data[" << seg.index << "], " << seg.shift << "u, "
<< std::setw(2) << std::setfill('0') << "0x" << std::hex << seg.mask
<< std::dec << "u);" << std::setw(0) << std::setfill(' ')
<< std::endl;
}
if (sig.second.sign() == ad::dbcc::Sign::Signed)
{
uint32_t mask =
((1 << (sigHelper.typeLength() - sig.second.length())) - 1);
if (mask != 0)
{
const std::string indent2 =
ad::dbcc::generateIndent(indentLevel + 1);
const std::string conversionTypeSuffix =
sigHelper.conversionTypeSuffix();
mask <<= sig.second.length();
ss << indent << "if ((" << signalMemberName << "Temp & (1"
<< conversionTypeSuffix << " << " << (sig.second.length() - 1)
<< ")) != 0" << conversionTypeSuffix << ")" << std::endl
<< indent << "{" << std::endl
<< indent2 << signalMemberName << "Temp |= 0x" << std::hex
<< mask << std::dec << conversionTypeSuffix << ";" << std::endl
<< indent << "}" << std::endl;
}
}
ss << indent << "feedback->" << signalMemberName << " = ("
<< signalMemberType << ")(" << signalMemberName << "Temp * "
<< sig.second.factor() << " + (" << sig.second.offset() << ")"
<< ");";
return ss.str();
}
static std::string _signalPack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << sigHelper.operationTypeName() << " " << sig.first
<< "Temp = (" << sigHelper.operationTypeName() << ")((controller->"
<< sig.first << " - (" << sig.second.offset() << ")) / "
<< sig.second.factor() << ");" << std::endl;
for (auto seg : sigHelper.segments())
{
if (needNewLine)
{
ss << std::endl;
}
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << "frame->data[" << seg.index << "]"
<< " |= PACK_LEFT_SHIFT(";
}
else
{
ss << "frame->data[" << seg.index << "]"
<< " |= PACK_RIGHT_SHIFT(";
}
ss << sig.first << "Temp, " << seg.shift << "u, " << std::setw(2)
<< std::setfill('0') << "0x" << std::hex << seg.mask << std::dec
<< "u);" << std::setw(0) << std::setfill(' ');
needNewLine = true;
}
return ss.str();
}
static std::string _signalsUnpack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalUnpack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _signalsPack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << "memset(frame->data, 0, sizeof(frame->data));" << std::endl
<< std::endl;
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalPack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _messageUnpack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsUnpack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
static std::string _messagePack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsPack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
// Unpack for feedback
static std::string _generateUnpack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "int CanDataOperation_unpack(const CanFrame *frame, "
"Feedback *feedback)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame->id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(feedbackSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messageUnpack(msg, sigs, indentLevel + 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return 0;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return 1;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
// Pack for controller
static std::string _generatePack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "int CanDataOperation::pack(const ControlCommand "
"*controller, CanFrame *frame)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame->id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(controllerSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messagePack(msg, sigs, 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return 0;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return 1;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
static std::string _generateCHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#include <string.h> /**< memcpy memset */" << std::endl
<< std::endl
<< "#include \"can_data_operation.h\"" << std::endl
<< std::endl
<< PackAndUnpackMacros << std::endl
<< std::endl;
ss << std::endl
<< "void CanFrame_toCanData(struct CanFrame *o, struct can_frame *cf)\n"
"{\n"
" cf->can_id = o->id;\n"
" cf->can_dlc = (__u8)(o->length);\n"
" memcpy(cf.data, o->data, o->length);\n"
"}\n\n"
"void CanFrame_fromCanData(struct CamFrame *o, const struct "
"can_frame "
"*cf)\n"
"{\n"
" o->id = cf.can_id;\n"
" o->length = cf.can_dlc;\n"
" memcpy(o->data, cf.data, o->length);\n"
"}\n"
<< std::endl;
return ss.str();
}
static std::string _generateHHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#pragma once" << std::endl
<< std::endl
<< "#include <linux/can.h> /**< struct can_frame */" << std::endl
<< std::endl
<< "#include <stdint.h> /**< uint32_t uint16_t uint8_t */"
<< std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHDecl(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
std::stringstream ss;
std::string canFrame = std::string(
"struct CanFrame\n"
"{\n"
" uint32_t id;\n"
" uint32_t length;\n"
" uint8_t data[CAN_MAX_DLEN];\n"
"};\n\n"
"void CanFrame_toCanData(struct CanFrame *o, struct can_frame *cf);\n"
"void CanFrame_fromCanData(struct CamFrame *o, const struct can_frame "
"*cf);\n");
ss << canFrame << std::endl;
// Generate struct ad::bus::Feedback and struct ad::adbus::ControlCommand
// according to the configuration
NameSignalVector sigs;
ss << "struct Feedback" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
std::string signalMemberName = feedbackSignalName;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = feedbackSignalName.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = feedbackSignalName.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = feedbackSignalName.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< signalMemberName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
ss << "struct ControlCommand" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< controllerSignalName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
std::string definitions = std::string(
"\n"
"int CanDataOperation_unpack(const CanFrame *frame, Feedback "
"*feedback);\n"
"int CanDataOperation_pack(const ControlCommand *controller, "
"CanFrame *frame);\n"
"\n");
ss << definitions << std::endl;
return ss.str();
}
bool CGenerator::generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &path)
{
// If the path is empty, output the code to STDOUT
if (path.empty())
{
std::cout << _generateCHeader();
std::cout << _generateUnpack(iter, doc);
std::cout << std::endl;
std::cout << _generatePack(iter, doc) << std::endl;
std::cout << std::endl;
std::cout << _generateHHeader();
std::cout << _generateHDecl(iter, doc);
}
else
{
std::ofstream ofscpp(path + "/can_data_operation.c");
if (!ofscpp.good())
{
std::cerr << "Failed to generate " << path + "/can_data_operator.c"
<< std::endl;
return false;
}
ofscpp << _generateCHeader();
ofscpp << _generateUnpack(iter, doc);
ofscpp << std::endl;
ofscpp << _generatePack(iter, doc) << std::endl;
std::ofstream ofsh(path + "/can_data_operation.h");
if (!ofsh.good())
{
std::cerr << "Failed to generate " << path + "/can_data_operation.h"
<< std::endl;
return false;
}
ofsh << _generateHHeader();
ofsh << _generateHDecl(iter, doc);
}
return true;
}
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,16 @@
#pragma once
#include "generator_private.h"
namespace ad {
namespace dbcc {
class CGenerator : public GeneratorPrivate
{
public:
bool generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &outputPath) override;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,547 @@
#include "cpp_generator.h"
#include <iostream> /**< std::cout std::cerr std::endl std::hex std::dec */
#include <iomanip> /**< std::setw std::setfill */
#include <fstream> /**< std::ifstream */
namespace ad {
namespace dbcc {
static std::string _signalUnpack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
// Unpack the type information of this signal
std::string signalMemberName = sig.first;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = sig.first.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = sig.first.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = sig.first.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << indent << sigHelper.operationTypeName() << " " << signalMemberName
<< "Temp = 0;" << std::endl;
for (auto seg : sigHelper.segments(true))
{
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << signalMemberName << "Temp |= UNPACK_LEFT_SHIFT(";
}
else
{
ss << signalMemberName << "Temp |= UNPACK_RIGHT_SHIFT(";
}
ss << sigHelper.operationTypeName() << ", "
<< "frame.data[" << seg.index << "], " << seg.shift << "u, "
<< std::setw(2) << std::setfill('0') << "0x" << std::hex << seg.mask
<< std::dec << "u);" << std::setw(0) << std::setfill(' ')
<< std::endl;
}
if (sig.second.sign() == ad::dbcc::Sign::Signed)
{
uint32_t mask =
((1 << (sigHelper.typeLength() - sig.second.length())) - 1);
if (mask != 0)
{
const std::string indent2 =
ad::dbcc::generateIndent(indentLevel + 1);
const std::string conversionTypeSuffix =
sigHelper.conversionTypeSuffix();
mask <<= sig.second.length();
ss << indent << "if ((" << signalMemberName << "Temp & (1"
<< conversionTypeSuffix << " << " << (sig.second.length() - 1)
<< ")) != 0" << conversionTypeSuffix << ")" << std::endl
<< indent << "{" << std::endl
<< indent2 << signalMemberName << "Temp |= 0x" << std::hex
<< mask << std::dec << conversionTypeSuffix << ";" << std::endl
<< indent << "}" << std::endl;
}
}
ss << indent << "feedback." << signalMemberName << " = ("
<< signalMemberType << ")(" << signalMemberName << "Temp * "
<< sig.second.factor() << " + (" << sig.second.offset() << ")"
<< ");";
return ss.str();
}
static std::string _signalPack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << sigHelper.operationTypeName() << " " << sig.first
<< "Temp = (" << sigHelper.operationTypeName() << ")((controller."
<< sig.first << " - (" << sig.second.offset() << ")) / "
<< sig.second.factor() << ");" << std::endl;
for (auto seg : sigHelper.segments())
{
if (needNewLine)
{
ss << std::endl;
}
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_LEFT_SHIFT(";
}
else
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_RIGHT_SHIFT(";
}
ss << sig.first << "Temp, " << seg.shift << "u, " << std::setw(2)
<< std::setfill('0') << "0x" << std::hex << seg.mask << std::dec
<< "u);" << std::setw(0) << std::setfill(' ');
needNewLine = true;
}
return ss.str();
}
static std::string _signalsUnpack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalUnpack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _signalsPack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << "memset(frame.data, 0, sizeof(frame.data));" << std::endl
<< std::endl;
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalPack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _messageUnpack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsUnpack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
static std::string _messagePack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsPack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
// Unpack for feedback
static std::string _generateUnpack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(feedbackSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messageUnpack(msg, sigs, indentLevel + 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
// Pack for controller
static std::string _generatePack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::pack(const ad::canbus::ControlCommand "
"&controller, ad::canbus::CanFrame &frame)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(controllerSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messagePack(msg, sigs, 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
static std::string _generateCppHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#include <string> /**< std::string */" << std::endl
<< "#include <cstring> /**< memset */" << std::endl
<< std::endl
<< "#include \"can_data_operation.h\"" << std::endl
<< std::endl
<< PackAndUnpackMacros << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#pragma once" << std::endl
<< std::endl
<< "#include <linux/can.h> /**< struct can_frame */" << std::endl
<< std::endl
<< "#include <cstdint> /**< uint32_t uint16_t uint8_t */"
<< std::endl
<< "#include <cstring> /**< memcpy */" << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHDecl(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
std::stringstream ss;
std::string canFrame = std::string(
"struct CanFrame\n"
"{\n"
" uint32_t id;\n"
" uint32_t length;\n"
" uint8_t data[CAN_MAX_DLEN];\n\n"
" inline void toCanData(struct can_frame &cf)\n"
" {\n"
" cf.can_id = id;\n"
" cf.can_dlc = static_cast<__u8>(length);\n"
" memcpy(cf.data, data, length);\n"
" }\n\n"
" inline void fromCanData(const struct can_frame &cf)\n"
" {\n"
" id = cf.can_id;\n"
" length = cf.can_dlc;\n"
" memcpy(data, cf.data, length);\n"
" }\n"
"};\n");
ss << canFrame << std::endl;
// Generate struct ad::bus::Feedback and struct ad::adbus::ControlCommand
// according to the configuration
NameSignalVector sigs;
ss << "struct Feedback" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
std::string signalMemberName = feedbackSignalName;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = feedbackSignalName.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = feedbackSignalName.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = feedbackSignalName.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< signalMemberName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
ss << "struct ControlCommand" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< controllerSignalName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
std::string definitions = std::string(
"class CanDataOperation\n"
"{\n"
" static bool unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback);\n"
" static bool pack(const ad::canbus::ControlCommand &controller, "
"ad::canbus::CanFrame &frame);\n"
"};\n");
ss << definitions << std::endl;
return ss.str();
}
static std::string _generateHFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateCppFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
bool CppGenerator::generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &path)
{
// If the path is empty, output the code to STDOUT
if (path.empty())
{
std::cout << _generateCppHeader();
std::cout << _generateUnpack(iter, doc);
std::cout << std::endl;
std::cout << _generatePack(iter, doc) << std::endl;
std::cout << _generateCppFooter();
std::cout << std::endl;
std::cout << _generateHHeader();
std::cout << _generateHDecl(iter, doc);
std::cout << _generateHFooter();
}
else
{
std::ofstream ofscpp(path + "/can_data_operation.cpp");
if (!ofscpp.good())
{
std::cerr << "Failed to generate "
<< path + "/can_data_operator.cpp" << std::endl;
return false;
}
ofscpp << _generateCppHeader();
ofscpp << _generateUnpack(iter, doc);
ofscpp << std::endl;
ofscpp << _generatePack(iter, doc) << std::endl;
ofscpp << _generateCppFooter();
std::ofstream ofsh(path + "/can_data_operation.h");
if (!ofsh.good())
{
std::cerr << "Failed to generate " << path + "/can_data_operation.h"
<< std::endl;
return false;
}
ofsh << _generateHHeader();
ofsh << _generateHDecl(iter, doc);
ofsh << _generateHFooter();
}
return true;
}
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,16 @@
#pragma once
#include "generator_private.h"
namespace ad {
namespace dbcc {
class CppGenerator : public GeneratorPrivate
{
public:
bool generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &outputPath) override;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,548 @@
#include "csharp_generator.h"
#include <iostream> /**< std::cout std::cerr std::endl std::hex std::dec */
#include <iomanip> /**< std::setw std::setfill */
#include <fstream> /**< std::ifstream */
namespace ad {
namespace dbcc {
static std::string _signalUnpack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
// Unpack the type information of this signal
std::string signalMemberName = sig.first;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = sig.first.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = sig.first.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = sig.first.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << indent << sigHelper.operationTypeName() << " " << signalMemberName
<< "Temp = 0;" << std::endl;
for (auto seg : sigHelper.segments(true))
{
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << signalMemberName << "Temp |= UNPACK_LEFT_SHIFT(";
}
else
{
ss << signalMemberName << "Temp |= UNPACK_RIGHT_SHIFT(";
}
ss << sigHelper.operationTypeName() << ", "
<< "frame.data[" << seg.index << "], " << seg.shift << "u, "
<< std::setw(2) << std::setfill('0') << "0x" << std::hex << seg.mask
<< std::dec << "u);" << std::setw(0) << std::setfill(' ')
<< std::endl;
}
if (sig.second.sign() == ad::dbcc::Sign::Signed)
{
uint32_t mask =
((1 << (sigHelper.typeLength() - sig.second.length())) - 1);
if (mask != 0)
{
const std::string indent2 =
ad::dbcc::generateIndent(indentLevel + 1);
const std::string conversionTypeSuffix =
sigHelper.conversionTypeSuffix();
mask <<= sig.second.length();
ss << indent << "if ((" << signalMemberName << "Temp & (1"
<< conversionTypeSuffix << " << " << (sig.second.length() - 1)
<< ")) != 0" << conversionTypeSuffix << ")" << std::endl
<< indent << "{" << std::endl
<< indent2 << signalMemberName << "Temp |= 0x" << std::hex
<< mask << std::dec << conversionTypeSuffix << ";" << std::endl
<< indent << "}" << std::endl;
}
}
ss << indent << "feedback." << signalMemberName << " = ("
<< signalMemberType << ")(" << signalMemberName << "Temp * "
<< sig.second.factor() << " + (" << sig.second.offset() << ")"
<< ");";
return ss.str();
}
static std::string _signalPack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << sigHelper.operationTypeName() << " " << sig.first
<< "Temp = (" << sigHelper.operationTypeName() << ")((controller."
<< sig.first << " - (" << sig.second.offset() << ")) / "
<< sig.second.factor() << ");" << std::endl;
for (auto seg : sigHelper.segments())
{
if (needNewLine)
{
ss << std::endl;
}
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_LEFT_SHIFT(";
}
else
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_RIGHT_SHIFT(";
}
ss << sig.first << "Temp, " << seg.shift << "u, " << std::setw(2)
<< std::setfill('0') << "0x" << std::hex << seg.mask << std::dec
<< "u);" << std::setw(0) << std::setfill(' ');
needNewLine = true;
}
return ss.str();
}
static std::string _signalsUnpack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalUnpack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _signalsPack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << "memset(frame.data, 0, sizeof(frame.data));" << std::endl
<< std::endl;
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalPack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _messageUnpack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsUnpack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
static std::string _messagePack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsPack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
// Unpack for feedback
static std::string _generateUnpack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(feedbackSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messageUnpack(msg, sigs, indentLevel + 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
// Pack for controller
static std::string _generatePack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::pack(const ad::canbus::ControlCommand "
"&controller, ad::canbus::CanFrame &frame)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(controllerSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messagePack(msg, sigs, 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
static std::string _generateCppHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#include <string> /**< std::string */" << std::endl
<< "#include <cstring> /**< memset */" << std::endl
<< std::endl
<< "#include \"can_data_operation.h\"" << std::endl
<< std::endl
<< PackAndUnpackMacros << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#pragma once" << std::endl
<< std::endl
<< "#include <linux/can.h> /**< struct can_frame */" << std::endl
<< std::endl
<< "#include <cstdint> /**< uint32_t uint16_t uint8_t */"
<< std::endl
<< "#include <cstring> /**< memcpy */" << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHDecl(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
std::stringstream ss;
std::string canFrame = std::string(
"struct CanFrame\n"
"{\n"
" uint32_t id;\n"
" uint32_t length;\n"
" uint8_t data[CAN_MAX_DLEN];\n\n"
" inline void toCanData(struct can_frame &cf)\n"
" {\n"
" cf.can_id = id;\n"
" cf.can_dlc = static_cast<__u8>(length);\n"
" memcpy(cf.data, data, length);\n"
" }\n\n"
" inline void fromCanData(const struct can_frame &cf)\n"
" {\n"
" id = cf.can_id;\n"
" length = cf.can_dlc;\n"
" memcpy(data, cf.data, length);\n"
" }\n"
"};\n");
ss << canFrame << std::endl;
// Generate struct ad::bus::Feedback and struct ad::adbus::ControlCommand
// according to the configuration
NameSignalVector sigs;
ss << "struct Feedback" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
std::string signalMemberName = feedbackSignalName;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = feedbackSignalName.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = feedbackSignalName.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = feedbackSignalName.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< signalMemberName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
ss << "struct ControlCommand" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< controllerSignalName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
std::string definitions = std::string(
"class CanDataOperation\n"
"{\n"
" static bool unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback);\n"
" static bool pack(const ad::canbus::ControlCommand &controller, "
"ad::canbus::CanFrame &frame);\n"
"};\n");
ss << definitions << std::endl;
return ss.str();
}
static std::string _generateHFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateCppFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
bool CsharpGenerator::generate(DbcIterator &iter,
const rapidjson::Document &doc,
const std::string &path)
{
// If the path is empty, output the code to STDOUT
if (path.empty())
{
std::cout << _generateCppHeader();
std::cout << _generateUnpack(iter, doc);
std::cout << std::endl;
std::cout << _generatePack(iter, doc) << std::endl;
std::cout << _generateCppFooter();
std::cout << std::endl;
std::cout << _generateHHeader();
std::cout << _generateHDecl(iter, doc);
std::cout << _generateHFooter();
}
else
{
std::ofstream ofscpp(path + "/can_data_operation.cpp");
if (!ofscpp.good())
{
std::cerr << "Failed to generate "
<< path + "/can_data_operator.cpp" << std::endl;
return false;
}
ofscpp << _generateCppHeader();
ofscpp << _generateUnpack(iter, doc);
ofscpp << std::endl;
ofscpp << _generatePack(iter, doc) << std::endl;
ofscpp << _generateCppFooter();
std::ofstream ofsh(path + "/can_data_operation.h");
if (!ofsh.good())
{
std::cerr << "Failed to generate " << path + "/can_data_operation.h"
<< std::endl;
return false;
}
ofsh << _generateHHeader();
ofsh << _generateHDecl(iter, doc);
ofsh << _generateHFooter();
}
return true;
}
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,16 @@
#pragma once
#include "generator_private.h"
namespace ad {
namespace dbcc {
class CsharpGenerator : public GeneratorPrivate
{
public:
bool generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &outputPath) override;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,70 @@
#include "generator_private.h"
#include <iostream> /**< std::cout std::cerr std::endl std::hex std::dec */
#include <fstream> /**< std::ifstream */
namespace ad {
namespace dbcc {
Generator::Generator(const std::string &dbc) : m_dbcFile(dbc), m_configFile("")
{}
Generator::Generator(const std::string &dbc, const std::string &config)
: m_dbcFile(dbc), m_configFile(config)
{}
Generator::~Generator() {}
bool Generator::generate(const std::string &path, Language lang)
{
if (m_dbcFile.empty())
{
std::cerr << "Required argument(dbc-file) is mssing!" << std::endl;
return false;
}
// Load dbc file.
DbcIterator iter(m_dbcFile);
bool needFilter = false;
rapidjson::Document doc;
// Load json configuration if existent.
const std::vector<std::string> requiredKeys{"controller", "feedback"};
if (!m_configFile.empty())
{
std::ifstream ifs(m_configFile.c_str());
if (!ifs.good())
{
// Maybe the fail is not readable, not existent etc.
std::cerr << "Failed to load the configuration file: "
<< m_configFile << "!" << std::endl;
return false;
}
rapidjson::IStreamWrapper isw(ifs);
doc.ParseStream(isw);
// Check the required keys
for (auto &s : requiredKeys)
{
if (!doc.HasMember(s.c_str()))
{
std::cerr << "Required key <" << s << "> is not existent!"
<< std::endl;
return false;
}
}
needFilter = true;
}
auto *generator = GeneratorFactory::create(lang);
bool ret = generator->generate(iter, doc, path);
delete generator;
return ret;
}
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,47 @@
#include "generator_private.h"
#include "c_generator.h"
#include "cpp_generator.h"
#include "csharp_generator.h"
#include "rust_generator.h"
#include "java_generator.h"
#include "js_generator.h"
namespace ad {
namespace dbcc {
GeneratorPrivate *GeneratorFactory::create(Generator::Language lang)
{
if (lang == Generator::Language::C)
{
return new CGenerator();
}
else if (lang == Generator::Language::CPlusPlus)
{
return new CppGenerator();
}
else if (lang == Generator::Language::CSharp)
{
return new CsharpGenerator();
}
else if (lang == Generator::Language::Rust)
{
return new RustGenerator();
}
else if (lang == Generator::Language::Java)
{
return new JavaGenerator();
}
else if (lang == Generator::Language::Js)
{
return new JsGenerator();
}
else
{
std::cerr << "Unsupported language: " << (int)lang << std::endl;
return nullptr;
}
}
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,89 @@
#pragma once
#include <string> /**< std::string */
#include <vector> /**< std::vector */
#include <utility> /**< std::pair */
#include <sstream> /**< std::stringstream */
#include <cctype> /**< std::isspace */
#include <algorithm> /**< std::find_if */
#include "dbcc/generator.h"
#include "dbcc/dbc_iterator.h" /**< ad::dbcc::DbcIterator */
#include "dbcc/message.h" /**< ad::dbcc::Message */
#include "dbcc/helper/signal_helper.h"
#include "rapidjson/document.h" /**< rapidjson::Document */
#include "rapidjson/istreamwrapper.h" /**< rapidjson::IStreamWrapper */
namespace ad {
namespace dbcc {
typedef std::pair<std::string, ad::dbcc::Signal> NameSignalPair;
typedef std::vector<NameSignalPair> NameSignalVector;
const std::string DefaultIndentSpaces = " ";
/// C/C++
const std::string PackAndUnpackMacros = std::string(
"#define PACK_LEFT_SHIFT(value,shift,mask) (uint8_t)((uint8_t)(value << "
"shift) & mask)\n"
"#define UNPACK_LEFT_SHIFT(_type,value,shift,mask) (_type)((_type)(value & "
"mask) << shift)\n"
"\n"
"#define PACK_RIGHT_SHIFT(value,shift,mask) (uint8_t)((uint8_t)(value >> "
"shift) & mask)\n"
"#define UNPACK_RIGHT_SHIFT(_type,value,shift,mask) (_type)((_type)(value "
"& mask) >> shift)\n");
const std::string DeclarationInfo = std::string(
"/** !!! The file is generated automatically, PLEASE DON'T MODIFY MANUALLY "
"!!! */");
inline void ltrim(std::string &s)
{
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
[](int ch) { return !std::isspace(ch); }));
}
inline void rtrim(std::string &s)
{
s.erase(std::find_if(s.rbegin(), s.rend(),
[](int ch) { return !std::isspace(ch); })
.base(),
s.end());
}
inline void trim(std::string &s)
{
ltrim(s);
rtrim(s);
}
inline const std::string generateIndent(int indentLevel)
{
std::stringstream ss;
for (int i = 0; i < indentLevel; i++)
{
ss << DefaultIndentSpaces;
}
return ss.str();
}
class GeneratorPrivate
{
public:
virtual bool generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &outputPath) = 0;
virtual ~GeneratorPrivate() {}
};
class GeneratorFactory
{
public:
static GeneratorPrivate *create(Generator::Language lang);
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,547 @@
#include "java_generator.h"
#include <iostream> /**< std::cout std::cerr std::endl std::hex std::dec */
#include <iomanip> /**< std::setw std::setfill */
#include <fstream> /**< std::ifstream */
namespace ad {
namespace dbcc {
static std::string _signalUnpack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
// Unpack the type information of this signal
std::string signalMemberName = sig.first;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = sig.first.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = sig.first.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = sig.first.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << indent << sigHelper.operationTypeName() << " " << signalMemberName
<< "Temp = 0;" << std::endl;
for (auto seg : sigHelper.segments(true))
{
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << signalMemberName << "Temp |= UNPACK_LEFT_SHIFT(";
}
else
{
ss << signalMemberName << "Temp |= UNPACK_RIGHT_SHIFT(";
}
ss << sigHelper.operationTypeName() << ", "
<< "frame.data[" << seg.index << "], " << seg.shift << "u, "
<< std::setw(2) << std::setfill('0') << "0x" << std::hex << seg.mask
<< std::dec << "u);" << std::setw(0) << std::setfill(' ')
<< std::endl;
}
if (sig.second.sign() == ad::dbcc::Sign::Signed)
{
uint32_t mask =
((1 << (sigHelper.typeLength() - sig.second.length())) - 1);
if (mask != 0)
{
const std::string indent2 =
ad::dbcc::generateIndent(indentLevel + 1);
const std::string conversionTypeSuffix =
sigHelper.conversionTypeSuffix();
mask <<= sig.second.length();
ss << indent << "if ((" << signalMemberName << "Temp & (1"
<< conversionTypeSuffix << " << " << (sig.second.length() - 1)
<< ")) != 0" << conversionTypeSuffix << ")" << std::endl
<< indent << "{" << std::endl
<< indent2 << signalMemberName << "Temp |= 0x" << std::hex
<< mask << std::dec << conversionTypeSuffix << ";" << std::endl
<< indent << "}" << std::endl;
}
}
ss << indent << "feedback." << signalMemberName << " = ("
<< signalMemberType << ")(" << signalMemberName << "Temp * "
<< sig.second.factor() << " + (" << sig.second.offset() << ")"
<< ");";
return ss.str();
}
static std::string _signalPack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << sigHelper.operationTypeName() << " " << sig.first
<< "Temp = (" << sigHelper.operationTypeName() << ")((controller."
<< sig.first << " - (" << sig.second.offset() << ")) / "
<< sig.second.factor() << ");" << std::endl;
for (auto seg : sigHelper.segments())
{
if (needNewLine)
{
ss << std::endl;
}
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_LEFT_SHIFT(";
}
else
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_RIGHT_SHIFT(";
}
ss << sig.first << "Temp, " << seg.shift << "u, " << std::setw(2)
<< std::setfill('0') << "0x" << std::hex << seg.mask << std::dec
<< "u);" << std::setw(0) << std::setfill(' ');
needNewLine = true;
}
return ss.str();
}
static std::string _signalsUnpack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalUnpack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _signalsPack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << "memset(frame.data, 0, sizeof(frame.data));" << std::endl
<< std::endl;
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalPack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _messageUnpack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsUnpack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
static std::string _messagePack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsPack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
// Unpack for feedback
static std::string _generateUnpack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(feedbackSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messageUnpack(msg, sigs, indentLevel + 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
// Pack for controller
static std::string _generatePack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::pack(const ad::canbus::ControlCommand "
"&controller, ad::canbus::CanFrame &frame)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(controllerSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messagePack(msg, sigs, 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
static std::string _generateCppHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#include <string> /**< std::string */" << std::endl
<< "#include <cstring> /**< memset */" << std::endl
<< std::endl
<< "#include \"can_data_operation.h\"" << std::endl
<< std::endl
<< PackAndUnpackMacros << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#pragma once" << std::endl
<< std::endl
<< "#include <linux/can.h> /**< struct can_frame */" << std::endl
<< std::endl
<< "#include <cstdint> /**< uint32_t uint16_t uint8_t */"
<< std::endl
<< "#include <cstring> /**< memcpy */" << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHDecl(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
std::stringstream ss;
std::string canFrame = std::string(
"struct CanFrame\n"
"{\n"
" uint32_t id;\n"
" uint32_t length;\n"
" uint8_t data[CAN_MAX_DLEN];\n\n"
" inline void toCanData(struct can_frame &cf)\n"
" {\n"
" cf.can_id = id;\n"
" cf.can_dlc = static_cast<__u8>(length);\n"
" memcpy(cf.data, data, length);\n"
" }\n\n"
" inline void fromCanData(const struct can_frame &cf)\n"
" {\n"
" id = cf.can_id;\n"
" length = cf.can_dlc;\n"
" memcpy(data, cf.data, length);\n"
" }\n"
"};\n");
ss << canFrame << std::endl;
// Generate struct ad::bus::Feedback and struct ad::adbus::ControlCommand
// according to the configuration
NameSignalVector sigs;
ss << "struct Feedback" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
std::string signalMemberName = feedbackSignalName;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = feedbackSignalName.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = feedbackSignalName.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = feedbackSignalName.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< signalMemberName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
ss << "struct ControlCommand" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< controllerSignalName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
std::string definitions = std::string(
"class CanDataOperation\n"
"{\n"
" static bool unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback);\n"
" static bool pack(const ad::canbus::ControlCommand &controller, "
"ad::canbus::CanFrame &frame);\n"
"};\n");
ss << definitions << std::endl;
return ss.str();
}
static std::string _generateHFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateCppFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
bool JavaGenerator::generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &path)
{
// If the path is empty, output the code to STDOUT
if (path.empty())
{
std::cout << _generateCppHeader();
std::cout << _generateUnpack(iter, doc);
std::cout << std::endl;
std::cout << _generatePack(iter, doc) << std::endl;
std::cout << _generateCppFooter();
std::cout << std::endl;
std::cout << _generateHHeader();
std::cout << _generateHDecl(iter, doc);
std::cout << _generateHFooter();
}
else
{
std::ofstream ofscpp(path + "/can_data_operation.cpp");
if (!ofscpp.good())
{
std::cerr << "Failed to generate "
<< path + "/can_data_operator.cpp" << std::endl;
return false;
}
ofscpp << _generateCppHeader();
ofscpp << _generateUnpack(iter, doc);
ofscpp << std::endl;
ofscpp << _generatePack(iter, doc) << std::endl;
ofscpp << _generateCppFooter();
std::ofstream ofsh(path + "/can_data_operation.h");
if (!ofsh.good())
{
std::cerr << "Failed to generate " << path + "/can_data_operation.h"
<< std::endl;
return false;
}
ofsh << _generateHHeader();
ofsh << _generateHDecl(iter, doc);
ofsh << _generateHFooter();
}
return true;
}
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,16 @@
#pragma once
#include "generator_private.h"
namespace ad {
namespace dbcc {
class JavaGenerator : public GeneratorPrivate
{
public:
bool generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &outputPath) override;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,547 @@
#include "js_generator.h"
#include <iostream> /**< std::cout std::cerr std::endl std::hex std::dec */
#include <iomanip> /**< std::setw std::setfill */
#include <fstream> /**< std::ifstream */
namespace ad {
namespace dbcc {
static std::string _signalUnpack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
// Unpack the type information of this signal
std::string signalMemberName = sig.first;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = sig.first.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = sig.first.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = sig.first.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << indent << sigHelper.operationTypeName() << " " << signalMemberName
<< "Temp = 0;" << std::endl;
for (auto seg : sigHelper.segments(true))
{
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << signalMemberName << "Temp |= UNPACK_LEFT_SHIFT(";
}
else
{
ss << signalMemberName << "Temp |= UNPACK_RIGHT_SHIFT(";
}
ss << sigHelper.operationTypeName() << ", "
<< "frame.data[" << seg.index << "], " << seg.shift << "u, "
<< std::setw(2) << std::setfill('0') << "0x" << std::hex << seg.mask
<< std::dec << "u);" << std::setw(0) << std::setfill(' ')
<< std::endl;
}
if (sig.second.sign() == ad::dbcc::Sign::Signed)
{
uint32_t mask =
((1 << (sigHelper.typeLength() - sig.second.length())) - 1);
if (mask != 0)
{
const std::string indent2 =
ad::dbcc::generateIndent(indentLevel + 1);
const std::string conversionTypeSuffix =
sigHelper.conversionTypeSuffix();
mask <<= sig.second.length();
ss << indent << "if ((" << signalMemberName << "Temp & (1"
<< conversionTypeSuffix << " << " << (sig.second.length() - 1)
<< ")) != 0" << conversionTypeSuffix << ")" << std::endl
<< indent << "{" << std::endl
<< indent2 << signalMemberName << "Temp |= 0x" << std::hex
<< mask << std::dec << conversionTypeSuffix << ";" << std::endl
<< indent << "}" << std::endl;
}
}
ss << indent << "feedback." << signalMemberName << " = ("
<< signalMemberType << ")(" << signalMemberName << "Temp * "
<< sig.second.factor() << " + (" << sig.second.offset() << ")"
<< ");";
return ss.str();
}
static std::string _signalPack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << sigHelper.operationTypeName() << " " << sig.first
<< "Temp = (" << sigHelper.operationTypeName() << ")((controller."
<< sig.first << " - (" << sig.second.offset() << ")) / "
<< sig.second.factor() << ");" << std::endl;
for (auto seg : sigHelper.segments())
{
if (needNewLine)
{
ss << std::endl;
}
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_LEFT_SHIFT(";
}
else
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_RIGHT_SHIFT(";
}
ss << sig.first << "Temp, " << seg.shift << "u, " << std::setw(2)
<< std::setfill('0') << "0x" << std::hex << seg.mask << std::dec
<< "u);" << std::setw(0) << std::setfill(' ');
needNewLine = true;
}
return ss.str();
}
static std::string _signalsUnpack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalUnpack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _signalsPack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << "memset(frame.data, 0, sizeof(frame.data));" << std::endl
<< std::endl;
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalPack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _messageUnpack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsUnpack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
static std::string _messagePack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsPack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
// Unpack for feedback
static std::string _generateUnpack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(feedbackSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messageUnpack(msg, sigs, indentLevel + 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
// Pack for controller
static std::string _generatePack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::pack(const ad::canbus::ControlCommand "
"&controller, ad::canbus::CanFrame &frame)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(controllerSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messagePack(msg, sigs, 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
static std::string _generateCppHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#include <string> /**< std::string */" << std::endl
<< "#include <cstring> /**< memset */" << std::endl
<< std::endl
<< "#include \"can_data_operation.h\"" << std::endl
<< std::endl
<< PackAndUnpackMacros << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#pragma once" << std::endl
<< std::endl
<< "#include <linux/can.h> /**< struct can_frame */" << std::endl
<< std::endl
<< "#include <cstdint> /**< uint32_t uint16_t uint8_t */"
<< std::endl
<< "#include <cstring> /**< memcpy */" << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHDecl(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
std::stringstream ss;
std::string canFrame = std::string(
"struct CanFrame\n"
"{\n"
" uint32_t id;\n"
" uint32_t length;\n"
" uint8_t data[CAN_MAX_DLEN];\n\n"
" inline void toCanData(struct can_frame &cf)\n"
" {\n"
" cf.can_id = id;\n"
" cf.can_dlc = static_cast<__u8>(length);\n"
" memcpy(cf.data, data, length);\n"
" }\n\n"
" inline void fromCanData(const struct can_frame &cf)\n"
" {\n"
" id = cf.can_id;\n"
" length = cf.can_dlc;\n"
" memcpy(data, cf.data, length);\n"
" }\n"
"};\n");
ss << canFrame << std::endl;
// Generate struct ad::bus::Feedback and struct ad::adbus::ControlCommand
// according to the configuration
NameSignalVector sigs;
ss << "struct Feedback" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
std::string signalMemberName = feedbackSignalName;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = feedbackSignalName.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = feedbackSignalName.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = feedbackSignalName.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< signalMemberName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
ss << "struct ControlCommand" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< controllerSignalName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
std::string definitions = std::string(
"class CanDataOperation\n"
"{\n"
" static bool unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback);\n"
" static bool pack(const ad::canbus::ControlCommand &controller, "
"ad::canbus::CanFrame &frame);\n"
"};\n");
ss << definitions << std::endl;
return ss.str();
}
static std::string _generateHFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateCppFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
bool JsGenerator::generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &path)
{
// If the path is empty, output the code to STDOUT
if (path.empty())
{
std::cout << _generateCppHeader();
std::cout << _generateUnpack(iter, doc);
std::cout << std::endl;
std::cout << _generatePack(iter, doc) << std::endl;
std::cout << _generateCppFooter();
std::cout << std::endl;
std::cout << _generateHHeader();
std::cout << _generateHDecl(iter, doc);
std::cout << _generateHFooter();
}
else
{
std::ofstream ofscpp(path + "/can_data_operation.cpp");
if (!ofscpp.good())
{
std::cerr << "Failed to generate "
<< path + "/can_data_operator.cpp" << std::endl;
return false;
}
ofscpp << _generateCppHeader();
ofscpp << _generateUnpack(iter, doc);
ofscpp << std::endl;
ofscpp << _generatePack(iter, doc) << std::endl;
ofscpp << _generateCppFooter();
std::ofstream ofsh(path + "/can_data_operation.h");
if (!ofsh.good())
{
std::cerr << "Failed to generate " << path + "/can_data_operation.h"
<< std::endl;
return false;
}
ofsh << _generateHHeader();
ofsh << _generateHDecl(iter, doc);
ofsh << _generateHFooter();
}
return true;
}
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,16 @@
#pragma once
#include "generator_private.h"
namespace ad {
namespace dbcc {
class JsGenerator : public GeneratorPrivate
{
public:
bool generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &outputPath) override;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,33 @@
#include <iostream> /**< std::cout std::endl */
#include "dbcc/generator.h"
#include "dbcc/helper/signal_helper.h"
int main(int argc, char *argv[])
{
if (argc < 2)
{
std::cout << "Usage: dbcc <dbc-file> [json-config-file] [output-path]"
<< std::endl;
return 0;
}
if (argc < 3)
{
ad::dbcc::Generator gen{argv[1]};
gen.generate();
}
else
{
ad::dbcc::Generator gen{argv[1], argv[2]};
if (argc < 4)
{
gen.generate();
}
else
{
gen.generate(argv[3], (ad::dbcc::Generator::Language)atoi(argv[4]));
}
}
return 0;
}

View File

@ -0,0 +1,547 @@
#include "rust_generator.h"
#include <iostream> /**< std::cout std::cerr std::endl std::hex std::dec */
#include <iomanip> /**< std::setw std::setfill */
#include <fstream> /**< std::ifstream */
namespace ad {
namespace dbcc {
static std::string _signalUnpack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
// Unpack the type information of this signal
std::string signalMemberName = sig.first;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = sig.first.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = sig.first.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = sig.first.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << indent << sigHelper.operationTypeName() << " " << signalMemberName
<< "Temp = 0;" << std::endl;
for (auto seg : sigHelper.segments(true))
{
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << signalMemberName << "Temp |= UNPACK_LEFT_SHIFT(";
}
else
{
ss << signalMemberName << "Temp |= UNPACK_RIGHT_SHIFT(";
}
ss << sigHelper.operationTypeName() << ", "
<< "frame.data[" << seg.index << "], " << seg.shift << "u, "
<< std::setw(2) << std::setfill('0') << "0x" << std::hex << seg.mask
<< std::dec << "u);" << std::setw(0) << std::setfill(' ')
<< std::endl;
}
if (sig.second.sign() == ad::dbcc::Sign::Signed)
{
uint32_t mask =
((1 << (sigHelper.typeLength() - sig.second.length())) - 1);
if (mask != 0)
{
const std::string indent2 =
ad::dbcc::generateIndent(indentLevel + 1);
const std::string conversionTypeSuffix =
sigHelper.conversionTypeSuffix();
mask <<= sig.second.length();
ss << indent << "if ((" << signalMemberName << "Temp & (1"
<< conversionTypeSuffix << " << " << (sig.second.length() - 1)
<< ")) != 0" << conversionTypeSuffix << ")" << std::endl
<< indent << "{" << std::endl
<< indent2 << signalMemberName << "Temp |= 0x" << std::hex
<< mask << std::dec << conversionTypeSuffix << ";" << std::endl
<< indent << "}" << std::endl;
}
}
ss << indent << "feedback." << signalMemberName << " = ("
<< signalMemberType << ")(" << signalMemberName << "Temp * "
<< sig.second.factor() << " + (" << sig.second.offset() << ")"
<< ");";
return ss.str();
}
static std::string _signalPack2Code(NameSignalPair &sig, int indentLevel)
{
ad::dbcc::helper::SignalHelper sigHelper(sig.second);
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << sigHelper.operationTypeName() << " " << sig.first
<< "Temp = (" << sigHelper.operationTypeName() << ")((controller."
<< sig.first << " - (" << sig.second.offset() << ")) / "
<< sig.second.factor() << ");" << std::endl;
for (auto seg : sigHelper.segments())
{
if (needNewLine)
{
ss << std::endl;
}
ss << indent;
if (seg.direction ==
ad::dbcc::helper::SignalHelper::ShiftDirection::Left)
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_LEFT_SHIFT(";
}
else
{
ss << "frame.data[" << seg.index << "]"
<< " |= PACK_RIGHT_SHIFT(";
}
ss << sig.first << "Temp, " << seg.shift << "u, " << std::setw(2)
<< std::setfill('0') << "0x" << std::hex << seg.mask << std::dec
<< "u);" << std::setw(0) << std::setfill(' ');
needNewLine = true;
}
return ss.str();
}
static std::string _signalsUnpack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalUnpack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _signalsPack(const NameSignalVector &sigs, int indentLevel)
{
std::stringstream ss;
bool needNewLine = false;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
ss << indent << "memset(frame.data, 0, sizeof(frame.data));" << std::endl
<< std::endl;
for (auto sig : sigs)
{
if (needNewLine)
{
ss << std::endl << std::endl;
}
ss << indent << "/* " << sig.second.name() << ": "
<< sig.second.startBit() << ", " << sig.second.length() << " */"
<< std::endl;
ss << _signalPack2Code(sig, indentLevel);
needNewLine = true;
}
return ss.str();
}
static std::string _messageUnpack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsUnpack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
static std::string _messagePack(Message msg, const NameSignalVector &sigs,
int indentLevel)
{
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent2 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent << "/* " << msg.name() << ": " << msg.dlc() << " */"
<< std::endl;
ss << indent << "case 0x" << std::hex << msg.id() << std::dec << ":"
<< std::endl;
ss << indent2 << "{" << std::endl;
ss << _signalsPack(sigs, indentLevel + 2) << std::endl;
ss << indent2 << "}" << std::endl;
ss << indent2 << "break;";
return ss.str();
}
// Unpack for feedback
static std::string _generateUnpack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(feedbackSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messageUnpack(msg, sigs, indentLevel + 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
// Pack for controller
static std::string _generatePack(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
NameSignalVector sigs;
bool needNewLine = false;
std::stringstream ss;
const std::string indent = ad::dbcc::generateIndent(indentLevel);
const std::string indent1 = ad::dbcc::generateIndent(indentLevel + 1);
ss << indent
<< "bool CanDataOperation::pack(const ad::canbus::ControlCommand "
"&controller, ad::canbus::CanFrame &frame)"
<< std::endl
<< indent << "{" << std::endl
<< indent1 << "switch (frame.id)" << std::endl
<< indent1 << "{" << std::endl;
for (auto msg : iter)
{
sigs.clear();
for (auto sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
sigs.push_back(std::make_pair(controllerSignalName, sig));
}
}
// We need the signals in this message body
if (!sigs.empty())
{
if (needNewLine)
{
ss << std::endl;
}
ss << _messagePack(msg, sigs, 1) << std::endl;
needNewLine = true;
}
}
ss << indent1 << "default: return false;" << std::endl
<< indent1 << "}" << std::endl
<< indent1 << "return true;" << std::endl
<< indent << "}" << std::endl;
return ss.str();
}
static std::string _generateCppHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#include <string> /**< std::string */" << std::endl
<< "#include <cstring> /**< memset */" << std::endl
<< std::endl
<< "#include \"can_data_operation.h\"" << std::endl
<< std::endl
<< PackAndUnpackMacros << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHHeader()
{
std::stringstream ss;
ss << DeclarationInfo << std::endl
<< "#pragma once" << std::endl
<< std::endl
<< "#include <linux/can.h> /**< struct can_frame */" << std::endl
<< std::endl
<< "#include <cstdint> /**< uint32_t uint16_t uint8_t */"
<< std::endl
<< "#include <cstring> /**< memcpy */" << std::endl
<< std::endl
<< "namespace ad {" << std::endl
<< "namespace canbus {" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateHDecl(DbcIterator &iter,
const rapidjson::Document &doc,
int indentLevel = 0)
{
std::stringstream ss;
std::string canFrame = std::string(
"struct CanFrame\n"
"{\n"
" uint32_t id;\n"
" uint32_t length;\n"
" uint8_t data[CAN_MAX_DLEN];\n\n"
" inline void toCanData(struct can_frame &cf)\n"
" {\n"
" cf.can_id = id;\n"
" cf.can_dlc = static_cast<__u8>(length);\n"
" memcpy(cf.data, data, length);\n"
" }\n\n"
" inline void fromCanData(const struct can_frame &cf)\n"
" {\n"
" id = cf.can_id;\n"
" length = cf.can_dlc;\n"
" memcpy(data, cf.data, length);\n"
" }\n"
"};\n");
ss << canFrame << std::endl;
// Generate struct ad::bus::Feedback and struct ad::adbus::ControlCommand
// according to the configuration
NameSignalVector sigs;
ss << "struct Feedback" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["feedback"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string feedbackSignalName = std::string(
doc["feedback"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
std::string signalMemberName = feedbackSignalName;
std::string signalMemberType = sigHelper.typeName();
std::string::size_type commaPos = feedbackSignalName.find(",");
if (commaPos != std::string::npos)
{
std::stringstream tss;
signalMemberName = feedbackSignalName.substr(0, commaPos);
ad::dbcc::trim(signalMemberName);
std::stringstream().swap(tss);
signalMemberType = feedbackSignalName.substr(commaPos + 1);
ad::dbcc::trim(signalMemberType);
}
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< signalMemberName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
ss << "struct ControlCommand" << std::endl << "{" << std::endl;
for (auto &msg : iter)
{
sigs.clear();
for (auto &sig : msg)
{
if (!doc["controller"].HasMember(sig.name().c_str()))
{
continue;
}
else
{
std::string controllerSignalName = std::string(
doc["controller"][sig.name().c_str()].GetString());
ad::dbcc::helper::SignalHelper sigHelper(sig);
ss << DefaultIndentSpaces << sigHelper.typeName() << " "
<< controllerSignalName << ";" << std::endl;
}
}
}
ss << "};" << std::endl << std::endl;
std::string definitions = std::string(
"class CanDataOperation\n"
"{\n"
" static bool unpack(const ad::canbus::CanFrame &frame, "
"ad::canbus::Feedback &feedback);\n"
" static bool pack(const ad::canbus::ControlCommand &controller, "
"ad::canbus::CanFrame &frame);\n"
"};\n");
ss << definitions << std::endl;
return ss.str();
}
static std::string _generateHFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
static std::string _generateCppFooter()
{
std::stringstream ss;
ss << "} // namespace canbus" << std::endl
<< "} // namespace ad" << std::endl
<< std::endl;
return ss.str();
}
bool RustGenerator::generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &path)
{
// If the path is empty, output the code to STDOUT
if (path.empty())
{
std::cout << _generateCppHeader();
std::cout << _generateUnpack(iter, doc);
std::cout << std::endl;
std::cout << _generatePack(iter, doc) << std::endl;
std::cout << _generateCppFooter();
std::cout << std::endl;
std::cout << _generateHHeader();
std::cout << _generateHDecl(iter, doc);
std::cout << _generateHFooter();
}
else
{
std::ofstream ofscpp(path + "/can_data_operation.cpp");
if (!ofscpp.good())
{
std::cerr << "Failed to generate "
<< path + "/can_data_operator.cpp" << std::endl;
return false;
}
ofscpp << _generateCppHeader();
ofscpp << _generateUnpack(iter, doc);
ofscpp << std::endl;
ofscpp << _generatePack(iter, doc) << std::endl;
ofscpp << _generateCppFooter();
std::ofstream ofsh(path + "/can_data_operation.h");
if (!ofsh.good())
{
std::cerr << "Failed to generate " << path + "/can_data_operation.h"
<< std::endl;
return false;
}
ofsh << _generateHHeader();
ofsh << _generateHDecl(iter, doc);
ofsh << _generateHFooter();
}
return true;
}
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,16 @@
#pragma once
#include "generator_private.h"
namespace ad {
namespace dbcc {
class RustGenerator : public GeneratorPrivate
{
public:
bool generate(DbcIterator &iter, const rapidjson::Document &doc,
const std::string &outputPath) override;
};
} // namespace dbcc
} // namespace ad

View File

@ -0,0 +1,15 @@
cmake_minimum_required (VERSION 3.9)
project (parser)
include (admake)
set (TARGET_NAME ${CMAKE_PROJECT_NAME}${SUFFIX_STR})
append_modules (dbcc)
file (GLOB_RECURSE SRC
"${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
add_executable (${TARGET_NAME} ${SRC})
add_dependencies (${TARGET_NAME} ${DBCC_DEPS})
target_link_libraries (${TARGET_NAME} ${DBCC_LIB})

View File

@ -0,0 +1,20 @@
#include <iostream> /**< std::cout std::cerr std::endl */
#include <string> /**< std::string */
#include "dbcc/dbc_iterator.h"
const std::string usage = "This parser is meant to be used via CLi at the moment\n"
"\t./parser <FILE>\n";
int main(int argc, char *argv[])
{
if (argc < 2)
{
std::cout << usage << std::endl;
return 0;
}
ad::dbcc::DbcIterator iter(argv[1]);
std::cout << iter << std::endl;
return 0;
}

Some files were not shown because too many files have changed in this diff Show More