chore: migrate
This commit is contained in:
parent
f2c6c4935a
commit
63930a3d9b
|
@ -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/
|
||||
|
|
@ -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.
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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__])
|
|
@ -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
|
|
@ -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()
|
|
@ -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())
|
|
@ -0,0 +1,2 @@
|
|||
Cargo.lock
|
||||
/target
|
|
@ -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"] }
|
|
@ -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"
|
||||
```
|
||||
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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"]
|
|
@ -0,0 +1,3 @@
|
|||
*.exe
|
||||
Cargo.lock
|
||||
/target
|
|
@ -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
|
|
@ -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"
|
||||
}
|
|
@ -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(())
|
||||
}
|
|
@ -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}")
|
|
@ -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/
|
||||
|
|
@ -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 ()
|
||||
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.*
|
|
@ -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.
|
|
@ -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 });
|
||||
}));
|
|
@ -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"
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
});
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
|
@ -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];
|
||||
});
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
@ -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"]
|
|
@ -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");
|
||||
}
|
|
@ -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 |
|
@ -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 */
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <string> /**< std::string */
|
||||
|
||||
namespace ad {
|
||||
namespace dbcc {
|
||||
|
||||
std::string version();
|
||||
|
||||
} // namespace dbcc
|
||||
} // namespace ad
|
|
@ -0,0 +1 @@
|
|||
*/
|
|
@ -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
|
||||
```
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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,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]; }
|
||||
|
||||
%%
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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})
|
||||
|
|
@ -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
|
||||
...
|
|
@ -0,0 +1,4 @@
|
|||
.build/
|
||||
lib/
|
||||
bin/
|
||||
compile_commands.json
|
|
@ -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 ()
|
|
@ -0,0 +1,10 @@
|
|||
# Blf
|
||||
|
||||
## Features
|
||||
|
||||
## How to Build
|
||||
|
||||
## How to Run
|
||||
|
||||
## TODO
|
||||
|
|
@ -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")
|
|
@ -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
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#include <iostream> /**< std::cout std::endl */
|
||||
|
||||
int main(int /* argc */, char * /* argv */[])
|
||||
{
|
||||
std::cout << "blf" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
--index-url https://opentuna.cn/pypi/web/simple
|
||||
cantools
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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})
|
||||
|
|
@ -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
Loading…
Reference in New Issue