I'm totally new at app developing.
I trying to communicate with C-written end device via raw UDP packet. (such a modbus-like protocol)
And I'm suffering pain with serial/deserializing class(struct).
Here is a simple class, Packet which contain uint32, uint16, uint8.
Pack() is working, but are there any better way to achieve that?
I don't know how can I implement Unpack() method. I mean, how can I convert Uint8List to int?
import 'dart:typed_data';
void main() {
var pkt = new Packet();
pkt.TID = 0x01234567;
pkt.Src = 0x89;
pkt.Des = 0xab;
pkt.Data = 0xcdef;
var buf = pkt.Pack();
print('Packed: ${buf}');
var pkt_2 = new Packet();
if (pkt_2.Unpack(buf) != null) {
print("panic!");
return;
}
print('UnPacked: ${pkt_2.toString()}');
}
class Packet {
int TID; // uint32
int Src; // uint8
int Des; // uint8
int Data; // uint16
static const SIZE = 8;
String toString() {
return 'TID: ${TID.toRadixString(16)}, Src:${Src.toRadixString(16)}, Des:${Des.toRadixString(16)}, Data:${Data.toRadixString(16)}';
}
Uint8List Pack() {
var buf = Byteconv.itou32(TID) +
Byteconv.itou8(Src) +
Byteconv.itou8(Des) +
Byteconv.itou16(Data);
return Uint8List.fromList(buf);
}
Error Unpack(Uint8List buf) {
if (buf.length != SIZE) {
return Error();
}
// What can I do?
// TID =
// Src =
// Des =
// Data =
return null;
}
}
class Byteconv {
static Uint8List itou64(int val) {
return Uint8List(8)..buffer.asByteData().setUint64(0, val, Endian.big);
}
static Uint8List itou32(int val) {
return Uint8List(4)..buffer.asByteData().setUint32(0, val, Endian.big);
}
static Uint8List itou16(int val) {
return Uint8List(2)..buffer.asByteData().setUint16(0, val, Endian.big);
}
static Uint8List itou8(int u8) {
return Uint8List(1)..buffer.asUint8List()[0] = u8;
}
}
SOLVED
var buf_view = ByteData.sublistView(buf);
TID = buf_view.getUint32(0);
Src = buf_view.getUint8(4);
Des = buf_view.getUint8(5);
Data = buf_view.getUint16(6);
Related
We have this code on encrypting/decrypting a particular message and I'd like to decrypt the value in Flutter (dart).
/* Encrypt text
* #param text
*/
export const encrypt = (text: string): string => {
const encJson = CryptoJS.AES.encrypt(JSON.stringify(text), SECRET_KEY).toString();
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encJson));
};
/* Decrypt text
* #param ciphertext
*/
export const decrypt = (ciphertext: string): string => {
const decData = CryptoJS.enc.Base64.parse(ciphertext).toString(CryptoJS.enc.Utf8);
const bytes = CryptoJS.AES.decrypt(decData, SECRET_KEY).toString(CryptoJS.enc.Utf8);
return JSON.parse(bytes);
};
I have tried the example mentioned in this article but could not make it work.
https://medium.com/#chingsuehok/cryptojs-aes-encryption-decryption-for-flutter-dart-7ca123bd7464
I really appreciate if anyone can help or point me out on what to change on my code.
Current code:
String decryptAESCryptoJS(String encrypted, String passphrase) {
try {
Uint8List encryptedBytesWithSalt = base64.decode(encrypted);
Uint8List encryptedBytes =
encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
final salt = encryptedBytesWithSalt.sublist(8, 16);
var keyndIV = deriveKeyAndIV(passphrase, salt);
final key = encrypt.Key(keyndIV.item1);
final iv = encrypt.IV(keyndIV.item2);
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
final decrypted =
encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
return decrypted;
} catch (error) {
throw error;
}
}
Tuple2<Uint8List, Uint8List> deriveKeyAndIV(String passphrase, Uint8List salt) {
var password = createUint8ListFromString(passphrase);
Uint8List concatenatedHashes = Uint8List(0);
List<int> currentHash = Uint8List(0);
bool enoughBytesForKey = false;
Uint8List preHash = Uint8List(0);
while (!enoughBytesForKey) {
int preHashLength = currentHash.length + password.length + salt.length;
if (currentHash.length > 0)
preHash = Uint8List.fromList(currentHash + password + salt);
else
preHash = Uint8List.fromList(password + salt);
currentHash = md5.convert(preHash).bytes;
concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash);
if (concatenatedHashes.length >= 48) enoughBytesForKey = true;
}
var keyBtyes = concatenatedHashes.sublist(0, 32);
var ivBtyes = concatenatedHashes.sublist(32, 48);
return new Tuple2(keyBtyes, ivBtyes);
}
Uint8List createUint8ListFromString(String s) {
var ret = new Uint8List(s.length);
for (var i = 0; i < s.length; i++) {
ret[i] = s.codeUnitAt(i);
}
return ret;
}
Uint8List genRandomWithNonZero(int seedLength) {
final random = Random.secure();
const int randomMax = 245;
final Uint8List uint8list = Uint8List(seedLength);
for (int i = 0; i < seedLength; i++) {
uint8list[i] = random.nextInt(randomMax) + 1;
}
return uint8list;
}
The CryptoJS code unnecessarily Base64 encodes the ciphertext twice during encryption. So the most reasonable solution would be to fix the CryptoJS code.
In encrypt(), encJson is already the Base64 encoded ciphertext, i.e. the body of the encrypt() method should actually be:
return CryptoJS.AES.encrypt(JSON.stringify(text), SECRET_KEY).toString();
and analogously the body of decrypt():
const bytes = CryptoJS.AES.decrypt(ciphertext, SECRET_KEY).toString(CryptoJS.enc.Utf8);
return JSON.parse(bytes);
With this fix, successful decryption with the unmodified Dart code is possible.
If for some reason the CryptoJS code must not be changed, the Dart code in decryptAESCryptoJS() must also Base64 decode twice:
Uint8List encryptedBytesWithSalt = base64.decode(utf8.decode(base64.decode(encrypted)));
With this fix, the ciphertext of the unmodified CryptoJS code can be successfully decrypted.
I have a file that is larger than 512 bytes to send to an esp32. I'm sending "withoutResponse: false", the flutter_blue library does the split according to the mtu size without problems, but when it reaches 512 bytes it returns an error to write in characteristic. To solve this I have a function that splits the file and writes each 512 bytes.
Esp32 can send me files larger than 512 without doing anything. Can I send larger files without splitting?
Example of code or library that makes this possible
ok, I decided to switch to the flutter_reactive_ble library, and I developed code similar to this one. Basically I split the file into smaller packets of 4096 bytes (this was defined in my communication protocol with esp32), then I call the function that sends and it splits again the file into packets the size of mtu less 19 bytes that are from the header. In the last packet I "writeWithResponse" and wait for the response, if it responds ok, increment and send the next package and repit the processs until the end of file.
late StreamSubscription<List<int>>? subscribeStream;
List<int> bytesOfFile = [];
int _size = 0;
int _increment = 0;
List<int> _returnOfSubscribe = [];
int _indexOfUploadController = 0;
Future<void> subscribeCharacteristic() async {
subscribeStream = widget
.subscribeToCharacteristic(widget.rcharacteristic)
.listen((event) {
debugPrint("resposta${hexToString(event)}");
setState(() {
_returnOfSubscribe = event;
if (_returnOfSubscribe != 'code of confirm reception file') {
debugPrint('err');
} else {
_increment++;
actualize();
}
});
});
}
void actualize() async {
int splitSize = 4096;
Iterable<int> s;
int n = (bytesOfFile.length / splitSize).ceil();
//if is the last split
if (_increment >= n) {
if (_returnOfSubscribe == 'code of confirm end of file') {
debugPrint("success");
} else {
debugPrint("err");
}
return;
}
if ((_size + splitSize) < bytesOfFile.length) {
s = bytesOfFile.getRange(_size, _size + splitSize);
} else {
s = bytesOfFile.getRange(_size, bytesOfFile.length);
}
await writeLongData(s.toList());
_size += splitSize;
}
writeLongData(List<int> data) async {
int splitSize = mtuNotifier.value - 19;
Iterable<int> s;
int size = 0;
int n = (data.length / splitSize).ceil();
for (int i = 0; i < n; i++) {
if ((size + splitSize) < data.length) {
s = data.getRange(size, size + splitSize);
} else {
s = data.getRange(size, data.length);
}
try {
if ((size + splitSize) < data.length) {
await widget.writeWithoutResponse(widget.wcharacteristic, s.toList());
} else {
//if the last, write with response
await widget.writeWithResponse(widget.wcharacteristic, s.toList());
}
} catch (e) {
debugPrint('$e');
return;
}
size += splitSize;
}
}
I want my API to return an array of uint64 to my on-chain contract.
I tried 2 response formats for my API:
The array of uint64 itself (BN string here, but I need it in true uint64 not strings in my contract):
{"data":["629343835796877311","629343835797458943","629343835797471231"]}
concatenated hexadecimal strings (a new value every 16 chars):
{"data":"08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff"}
I discarded using the first approach because having ["629343835796877311","629343835797458943","629343835797471231"] as bytes is actually difficult to extract. I might be wrong! Maybe there is a base64 approach to encode and decode the data back into solidity data types, maybe?
I will use the second approach bellow.
Chainlink will pass the response as bytes memory _data:
function fulfill(bytes32 _requestId, bytes memory _data)
public
recordChainlinkFulfillment(_requestId)
{
data = string(_data);
}
Those bytes memory _data are successfully received and converted to a string (in storage data). The string value looks like this
08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff ...
In this example each 16 chars represent a uint64 number.
The first one: 08bbe0e25e412fff is 629343835796877311 for instance.
In solidity, I need to split the string each 16 chars and then convert it into their uint64 value.
I could use the bytes memory _data instead of the string(_data) if the code would be simpler or consume less gas. I am not sure
Please I need help with this I have been struggling.
Thanks
I got this contract working
COMMENTS:
the method hexBytesToInt is going to get a string representing hexa value like ffa0 for instance and return it's decimal value.
the method getSlice is just going to slice a string. In my case I have a new hexa value every 16 chars so I need to slice (0,16) than (16,32) etc...
the method hexStringToIntArray is managing the increments to slice every 16 chars and call the hexBytesToInt to transform the hex string in uint.
If you really want to dig into this solution, you are better off starting by understanding the test cases.
pragma solidity >=0.4.22 <0.8.11;
contract Serializer {
function hexStringToIntArray(string memory s) public pure returns (uint64[] memory) {
uint size = bytes(s).length / 16;
uint64[] memory result = new uint64[](size);
for (uint i = 0; i< size; i++) {
string memory strSlice = getSlice(i*16, (i+1)*16, s);
result[i] = hexStringToInt(strSlice);
}
return result;
}
function getSlice(uint startIndex, uint endIndex, string memory str) public pure returns (string memory) {
bytes memory strBytes = bytes(str);
bytes memory result = new bytes(endIndex-startIndex);
for(uint i = startIndex; i < endIndex; i++) {
result[i-startIndex] = strBytes[i];
}
return string(result);
}
function hexBytesToInt(bytes memory ss) public pure returns (uint64){
uint64 val = 0;
uint8 a = uint8(97); // a
uint8 zero = uint8(48); //0
uint8 nine = uint8(57); //9
uint8 A = uint8(65); //A
uint8 F = uint8(70); //F
uint8 f = uint8(102); //f
for (uint i=0; i<ss.length; ++i) {
uint8 byt = uint8(ss[i]);
if (byt >= zero && byt <= nine) byt = byt - zero;
else if (byt >= a && byt <= f) byt = byt - a + 10;
else if (byt >= A && byt <= F) byt = byt - A + 10;
val = (val << 4) | (byt & 0xF);
}
return val;
}
function hexStringToInt(string memory s) public pure returns (uint64) {
bytes memory ss = bytes(s);
uint64 val = hexBytesToInt(ss);
return val;
}
}
the tests:
const Serializer = artifacts.require("Serializer");
const truffleAssert = require("truffle-assertions");
const fs = require("fs");
const { readLines } = require("./utils.js");
const BN = web3.utils.BN;
contract("Serializer", (accounts) => {
const [deployerAddress, tokenHolderOneAddress, tokenHolderTwoAddress] = accounts;
it("hexStringToInt", async () => {
let s = await Serializer.deployed();
let result = await s.hexStringToInt.call("08bbe0e25e412fff");
let expected = new BN("629343835796877311");
assert.equal(result.toString(10), expected.toString(10));
result = await s.hexStringToInt.call("08bbe0e25e4a0fff");
expected = new BN("629343835797458943");
assert.equal(result.toString(10), expected.toString(10));
result = await s.hexStringToInt.call("08bbe0e25e4a3fff");
expected = new BN("629343835797471231");
assert.equal(result.toString(10), expected.toString(10));
});
it("getSlice1", async () => {
let s = await Serializer.deployed();
let result = await s.getSlice.call(0, 16, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
let expected = "08bbe0e25e412fff";
assert.equal(result, expected);
});
it("getSlice2", async () => {
let s = await Serializer.deployed();
const result = await s.getSlice.call(16, 32, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
const expected = "08bbe0e25e4a0fff";
assert.equal(result, expected);
});
it("getSlice3", async () => {
let s = await Serializer.deployed();
const result = await s.getSlice.call(32, 48, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
const expected = "08bbe0e25e4a3fff";
assert.equal(result, expected);
});
it("hexStringToIntArray", async () => {
let s = await Serializer.deployed();
let result = await s.hexStringToIntArray.call("08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
console.log(result);
let expected = [
new BN("629343835796877311").toString(),
new BN("629343835797458943").toString(),
new BN("629343835797471231").toString(),
];
const resultS = result.map((x) => x.toString());
assert.deepEqual(resultS, expected);
});
});
I am trying to wrap the libcups library https://github.com/apple/cups to be used in my Swift project.
I have tried some of the examples in https://www.cups.org/doc/cupspm.html and they are working fine.
However I am struggling when it comes to wrapping the C code to be used in a Swift project.
I have been searching online on how to wrap C libraries in Swift but has not been able to have much progress.
Here is the C code
#include <stdio.h>
#include <cups/cups.h>
typedef struct {
int num_dests;
cups_dest_t *dests;
} my_user_data_t;
int my_dest_cb(my_user_data_t *user_data, unsigned flags, cups_dest_t *dest) {
if (flags & CUPS_DEST_FLAGS_REMOVED) {
user_data->num_dests = cupsRemoveDest(dest->name, dest->instance, user_data->num_dests, &(user_data->dests));
} else {
user_data->num_dests = cupsCopyDest(dest, user_data->num_dests, &(user_data->dests));
}
return 1;
}
int my_get_dests(cups_ptype_t type, cups_ptype_t mask, cups_dest_t **dests) {
my_user_data_t user_data = { 0, NULL };
if (!cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, type, mask, (cups_dest_cb_t)my_dest_cb, &user_data)) {
cupsFreeDests(user_data.num_dests, user_data.dests);
*dests = NULL;
return 0;
} else {
*dests = user_data.dests;
return user_data.num_dests;
}
}
int main(int argc, const char * argv[]) {
cups_dest_t *dests = NULL;
int num_dests = my_get_dests(0, 0, &dests);
printf("Destination found: %d\n", num_dests);
cups_dest_t *dest;
int i;
const char *value;
for (i = num_dests, dest = dests; i > 0; i--, dest++) {
if (dest->instance == NULL) {
value = cupsGetOption("printer-info", dest->num_options, dest->options);
printf("%s (%s)\n", dest->name, value ? value : "No description");
}
}
return 0;
}
Here is the same thing but in Swift
let destinationsCallback: cups_dest_cb_t = { user_data, flags, dest in
// (void *user_data, unsigned flags, cups_dest_t *dest)
var userDataPointer = user_data!.assumingMemoryBound(to: my_user_data_t.self).pointee
var destData = dest!.pointee
if destData.instance != nil {
print("\(String(cString: destData.name))/\(String(cString: destData.instance))")
} else {
print(String(cString: destData.name))
}
if flags == CUPS_DEST_FLAGS_REMOVED {
userDataPointer.num_dests = cupsRemoveDest(destData.name, destData.instance, userDataPointer.num_dests, &(userDataPointer.dests))
} else {
userDataPointer.num_dests = cupsCopyDest(dest, userDataPointer.num_dests, &(userDataPointer.dests))
}
return 1
}
func getDestinations(type: UInt32, mask: UInt32, dests: UnsafeMutablePointer<cups_dest_t>) -> Int32 {
var userData = my_user_data_t(num_dests: 0, dests: nil)
if cupsEnumDests(UInt32(CUPS_DEST_FLAGS_NONE), 1000, nil, type, mask, destinationsCallback, &userData) != 1 {
return 0
} else {
return userData.num_dests
}
}
I am not able to get the userData to return the correct value which I am assuming is due to the way I handle the pointers.
Greatly appreciate if I am able to get some advice.
IMHO the best way to work with such C libraries in Swift is to work with them in Objective-C. Nevertheless, the main issue seems to be that you are not writing back changes to userData, that's why you always get your initial value. You need to update it in your callback like this
let userDataPointer = user_data!.assumingMemoryBound(to: my_user_data_t.self)
var userData = userDataPointer.pointee
...
// make some changes to userData
userDataPointer.pointee = userData
return 1
Also there seems to be some differences between your C code and the Swift one, like checking flags with 'flags == CUPS_DEST_FLAGS_REMOVED' which, in general, is incorrect way to check flags, and comparing result of cupsEnumDests with 1 when in the original code you check that result is not 0.
Here is my code
void main() {
String phoneNumber = '123456789';
String formattedPhoneNumber = phoneNumber.replaceFirst("(\d{3})(\d{3})(\d+)", "(\$1) \$2-\$3");
print('Formatted number ${formattedPhoneNumber}');
}
Output:
Formatted number 123456789
I want output as Formatted number (123) 456-6789
Try this
print('1234567890'.replaceAllMapped(RegExp(r'(\d{3})(\d{3})(\d+)'), (Match m) => "(${m[1]}) ${m[2]}-${m[3]}"));
Create a custom Masked class
import 'package:flutter/material.dart';
class MaskedTextController extends TextEditingController {
MaskedTextController({String text, this.mask, Map<String, RegExp> translator})
: super(text: text) {
this.translator = translator ?? MaskedTextController.getDefaultTranslator();
this.addListener(() {
var previous = this._lastUpdatedText;
if (this.beforeChange(previous, this.text)) {
this.updateText(this.text);
this.afterChange(previous, this.text);
} else {
this.updateText(this._lastUpdatedText);
}
});
this.updateText(this.text);
}
String mask;
Map<String, RegExp> translator;
Function afterChange = (String previous, String next) {};
Function beforeChange = (String previous, String next) {
return true;
};
String _lastUpdatedText = '';
void updateText(String text) {
if(text != null){
this.text = this._applyMask(this.mask, text);
}
else {
this.text = '';
}
this._lastUpdatedText = this.text;
}
void updateMask(String mask, {bool moveCursorToEnd = true}) {
this.mask = mask;
this.updateText(this.text);
if (moveCursorToEnd) {
this.moveCursorToEnd();
}
}
void moveCursorToEnd() {
var text = this._lastUpdatedText;
this.selection = new TextSelection.fromPosition(
new TextPosition(offset: (text ?? '').length));
}
#override
void set text(String newText) {
if (super.text != newText) {
super.text = newText;
this.moveCursorToEnd();
}
}
static Map<String, RegExp> getDefaultTranslator() {
return {
'A': new RegExp(r'[A-Za-z]'),
'0': new RegExp(r'[0-9]'),
'#': new RegExp(r'[A-Za-z0-9]'),
'*': new RegExp(r'.*')
};
}
String _applyMask(String mask, String value) {
String result = '';
var maskCharIndex = 0;
var valueCharIndex = 0;
while (true) {
// if mask is ended, break.
if (maskCharIndex == mask.length) {
break;
}
// if value is ended, break.
if (valueCharIndex == value.length) {
break;
}
var maskChar = mask[maskCharIndex];
var valueChar = value[valueCharIndex];
// value equals mask, just set
if (maskChar == valueChar) {
result += maskChar;
valueCharIndex += 1;
maskCharIndex += 1;
continue;
}
// apply translator if match
if (this.translator.containsKey(maskChar)) {
if (this.translator[maskChar].hasMatch(valueChar)) {
result += valueChar;
maskCharIndex += 1;
}
valueCharIndex += 1;
continue;
}
// not masked value, fixed char on mask
result += maskChar;
maskCharIndex += 1;
continue;
}
return result;
}
}
Now call it in your main dart file
var maskedController = MaskedTextController(mask: '(000) 000-0000');
TextField(
controller: maskedController,
style: Styles.textNormalStyle,
maxLines: 1,
),
This solution work for your this specific Question and scenario.
you can achieve using following code.
String formattedPhoneNumber = "(" + phoneNumber.substring(0,3) + ") " +
phoneNumber.substring(3,6) + "-" +phoneNumber.substring(6,phoneNumber.length);
Ricardo pointed to a great library but his answer is half right. Besides the intl_phone_number_input you need to get libphonenumber_plugin installed as well.
intl_phone_number_input: ^0.7.0+2
libphonenumber_plugin:
The method getRegionInfoFromPhoneNumber "discovers" what country the number is from eg +55... it would interpret as it's from Brasil and proceed to format the phone number accordingly. You can also explicitly tell from where the phone number is from passing the country's acronym into the method eg. await PhoneNumber.getRegionInfoFromPhoneNumber(phone, "US"); It'll disregard a country code if it doesn't fit the number you're entering.
String phone = "+19795555555";
PhoneNumber number =
await PhoneNumber.getRegionInfoFromPhoneNumber(phone);
String formattedNumber = await PhoneNumberUtil.formatAsYouType(
number.phoneNumber!,
number.isoCode!,
);
print(formattedNumber); // -> prints: '+1 979-555-5555'
Also you can use: https://pub.dev/packages/intl_phone_number_input/example
String phoneNumber = '+234 500 500 5005';
PhoneNumber number = await PhoneNumber.getRegionInfoFromPhoneNumber(phoneNumber);
String parsableNumber = number.parseNumber();
`controller reference`.text = parsableNumber