I used the model with byte[] attribute that should be convert to short[] before used.
I add two properties to convert byte[] to short[] using get property and its work well.
my problem is when i try to use Set property to do the opposite and set byte[] array when changing the short[] my code not work and when i debug the code it doesn't call the Set Function at all.
public class TestModel
{
public byte[] Data { get; set; } // The original data
private short[] _DataConverted { get; set; }
public short[] DataConvert
{
get => _DataConverted ?? (_DataConverted = Data.GetShiftShort());
set
{
Data = value.GetShiftByteFShort();
_DataConverted = value;
}
}
}
public void Main()
{
TestModel modal =new TestModel()
{
Data = new byte[] {0,1,0,2,0,3}
};
modal.DataConvert[0] ++;
modal.DataConvert[1] +=1;
modal.DataConvert[2] = modal.DataConvert[2] + 1 ;
}
And the result is:
_DataConverted {2,3,4}
DataConvert {2,3,4}
But the Data don't change {0,1,0,2,0,3}
Related
I am trying to write one object to LiteDb and read it back.
I have tried following code but somehow all parameters of LiteDbParamEntry object are returned as NULL or zero.
public class LiteDbParamEntry
{
[BsonId]
public int Id
{
get { return (Index * 0x100) + SubIndex; }
}
public ushort Index;
public ushort SubIndex;
public string Text;
}
public class LiteDbParamValueStorage
{
private const string _liteDbPath = "MyLiteData.db";
public LiteDbParamValueStorage()
{
WriteEntry(123, 25);
ReadEntry(123, 25);
}
public void WriteEntry(ushort index, ushort subIndex)
{
using (var db = new LiteDatabase(_liteDbPath))
{
var entry = new LiteDbParamEntry {Index = index, SubIndex = subIndex, Text = "SomeText"};
var entries = db.GetCollection<LiteDbParamEntry>("LiteDbParamEntry");
entries.Insert(entry);
}
}
public void ReadEntry(ushort index, ushort subIndex)
{
using (var db = new LiteDatabase(_liteDbPath))
{
var collection = db.GetCollection<LiteDbParamEntry>("LiteDbParamEntry");
var paramEntry = collection.FindById((index * 0x100) + subIndex);
if (paramEntry != null)
Console.WriteLine(paramEntry.Text); //paramEntry.Text is returned as Null
else
return;
}
}
}
Can somebody please point me in right direction?
Finally, i've found what's wrong with your code... You forgot to add { get; set; } instruction on the right side of field declaration ;)
Take a look here:
public class LiteDbParamEntry
{
[BsonId]
public int Id
{
get { return (Index * 0x100) + SubIndex; }
}
public ushort Index { get; set; } //getter/setter added
public ushort SubIndex { get; set; } //getter/setter added
public string Text { get; set; } //getter/setter added
}
Good luck!
Im making an Xamarin.Android application that will use "templates" , template is this:
[Table("Templates")]
public class Template
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Category { get; set; }
//[TextBlob("imagesBlobbed")]
[OneToMany, Unique]
public List<TemplateImage> TemplateImages { get; set; }
public string ImagesHash { get; set; }
//public string imagesBlobbed { get; set; }
}
[Table("TemplateImages")]
public class TemplateImage
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Category { get; set; }
public string ImagesHash { get; set; }
public int Image { get; set; }
[ForeignKey(typeof(Template))]
public int TemplateId { get; set; }
}
i want all the TemplateImages objects to be unique in my database, the attribute Unique doesnt do anything, cause i guess because TemplateImage table has an auto increment Id will always be unique, no matter if the Image or ImageHash is the same in 2 records.
I was thinking then how else i can be sure that TemplateImages will be unique (Notice: when i say unique i mean that there isn't any other List that have the exact Image for every TemplateImage, in the same order).
Also i was using the ResourceID of the images, as image, which is wrong cause they might change on every new compiled app update.
So i decided to make a hash md5 from the images. They only way that i have find to load resource images (my images are vector .xml files) in Xamarin.Android is by converting the resource to bitmap, and then convert the bitmap to byte, and then the byte to md5.
And also i should have a hash string for the template item too. so im creating an md5 hash for the template by combining all the byte[] of the images into one and then hasing that.
I create this crazy code for this job:
public static string GetMD5Hash(byte[] content)
{
using (var md5 = MD5.Create())
{
byte[] computedHash = md5.ComputeHash(Encoding.UTF8.GetBytes(BitConverter.ToString(content).Replace("-", "")));
return new System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary(computedHash).ToString();
}
}
private static SQLiteConnection instance;
public static SQLiteConnection db()
{
if (instance == null)
instance = new SQLiteConnection(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "TemplatesData.db"));
return instance;
}
public static void CloseConnection()
{
if (instance != null)
{
instance.Close();
instance.Dispose();
instance = null;
}
}
}
public class TemplateDB
{
public static byte[] ConcatByteList(List<byte[]> list)
{
return list
.SelectMany(a => a)
.ToArray();
}
public static Bitmap GetBitmapFromVectorDrawable(Context context, int drawableId)
{
Drawable drawable = ContextCompat.GetDrawable(context, drawableId);
if (Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.Lollipop)
{
drawable = (DrawableCompat.Wrap(drawable)).Mutate();
}
Bitmap bitmap = Bitmap.CreateBitmap(drawable.IntrinsicWidth,
drawable.IntrinsicWidth, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
drawable.SetBounds(0, 0, canvas.Width, canvas.Height);
drawable.Draw(canvas);
return bitmap;
}
public byte[] DrawableToByteArray(int resourceId)
{
var context = AppState.ApplicationState;
using (var bitmap = GetBitmapFromVectorDrawable(context, resourceId))
{
int size = bitmap.ByteCount;
byte[] byteArray = new byte[size];
ByteBuffer byteBuffer = ByteBuffer.Allocate(size);
bitmap.CopyPixelsToBuffer(byteBuffer);
byteBuffer.Rewind();
byteBuffer.Get(byteArray);
return byteArray;
}
}
public static void AddTemplate(int category, List<int> images)
{
var templateDB = new TemplateDB();
var imageByteList = new List<byte[]>();
foreach (int image in images)
{
imageByteList.Add(templateDB.DrawableToByteArray(image));
}
var tmpl = new Template()
{
Category = category,
};
var img1 = new TemplateImage()
{
Category = category,
Image = images[0],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[0]),
};
var img2 = new TemplateImage()
{
Category = category,
Image = images[1],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[1]),
};
var img3 = new TemplateImage()
{
Category = category,
Image = images[2],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[2]),
};
var img4 = new TemplateImage()
{
Category = category,
Image = images[3],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[3]),
};
var img5 = new TemplateImage()
{
Category = category,
Image = images[4],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[4]),
};
tmpl.TemplateImages = new List<TemplateImage>() { img1, img2, img3, img4, img5 };
tmpl.ImagesHash = DatabaseHelper.GetMD5Hash(ConcatByteList(imageByteList));
var result = DatabaseHelper.db().Query<TemplateImage>("Select * from Templates where ImagesHash=?", tmpl.ImagesHash);
if (result.Count == 0)
{
DatabaseHelper.db().InsertAll(tmpl.TemplateImages);
DatabaseHelper.db().Insert(tmpl);
DatabaseHelper.db().UpdateWithChildren(tmpl);
}
}
Which all of a sudden, gives out of memory exception.
After thinking for a while that i should stop programming and start belly dancing, i think that since im giving in sqlite AddTemplate function a list of ResourceIds that i have to manually create (can't avoid it), then why not to give them my own string hash code and compare that to find if a record exist?
What is the correct approach?
In the end, i was thinking to use guid as extra id fields and add the parameter unique,in that case i could be sure which templates have been inserted, but i decided to use transactions. With transactions i can know that all my data is created or none and also save the creation into a variable in SharedPreferences and avoid recreating or even checking if the database exist.
For checking if the data is correctly updated and no recreation is needed i used this code:
public bool FirstRun { get; set; } = true;
public int DatabaseCreatedVersionOf { get; set; } = -1;
public override void OnCreate()
{
base.OnCreate();
}
public void UpdateDatabaseCreatedVersion()
{
DatabaseCreatedVersionOf = Preferences.Get(Settings.DatabaseCreatedVersionOfKey,
Settings.DatabaseCreatedVersionOfDefault);
}
public void CreateTemplateDB()
{
UpdateDatabaseCreatedVersion();
if (DatabaseCreatedVersionOf == -1)
TemplateDB.CreateDB();
FirstRun = false;
}
public Template GetTemplateById(int id)
{
if (FirstRun)
{
CreateTemplateDB();
FirstRun = false;
}
return TemplateDB.GetTemplate(id);
}
public List<Template> GetAllTemplates()
{
if (FirstRun)
{
CreateTemplateDB();
FirstRun = false;
}
return TemplateDB.GetAllTemplates();
}
Now all i have to do is call GetTemplateById or GetAllTemplates and if any creation is required, it will happen.
i need to write some data to a HDF5 file, i have a list of objects of the class "OPC_UA" inside a "BV" class, i want to create a dataset with the name of the BV and inside the dataset i want to have the list of the "OPC_UA" objects, tha OPC_UA Class have 3 attributes, a Long int ,one float value and a Datetime that i will convert to a timestamp, i can't seem to make it work, the Marshal.SizeOf() doesn't work with classes but i cant do it as a struct... Here is my code:
public void CriaHDF5Customizado(PackingConfigFile pf)
{
H5FileId fileId = H5F.create("pmdfq.h5", H5F.CreateMode.ACC_TRUNC);
H5GroupId infoGroupId = H5G.create(fileId, "informations");
H5G.close(infoGroupId);
H5GroupId datasetGroupId = H5G.create(fileId, "datasets");
long[] dims = new long[1];
foreach(BasicVariable bv in pf.basicVariableList.bvList)
{
OPC_UA aux = new OPC_UA();
var xx = bv.bvData;
int tamanho = Marshal.SizeOf(typeof(OPC_UA));
dims[0] = (long)bv.bvData.Count;
// dims[1] = (long)4;
H5DataSpaceId spaceId = H5S.create(H5S.H5SClass.SCALAR);
H5DataTypeId dataTypeId = H5T.create(H5T.CreateClass.COMPOUND, Marshal.SizeOf(typeof(OPC_UA)));
H5T.insert(dataTypeId, "TimeStamp", 0, new H5DataTypeId(H5T.H5Type.NATIVE_UINT));
H5T.insert(dataTypeId, "Quality", Marshal.SizeOf(H5T.H5Type.NATIVE_UINT), new H5DataTypeId(H5T.H5Type.NATIVE_UINT));
H5T.insert(dataTypeId, "Value", 2* Marshal.SizeOf(H5T.H5Type.NATIVE_UINT), new H5DataTypeId(H5T.H5Type.NATIVE_INT));
H5DataSetId dataSetId = H5D.create(datasetGroupId, bv.bvTag, dataTypeId, spaceId);
//H5D.write(dataSetId, new H5DataTypeId(H5T.H5Type.STD_REF_OBJ), new H5Array<OPC_UA>(bv.bvData.ToArray()));
H5D.writeScalar(dataSetId, dataTypeId, ref xx);
H5D.close(dataSetId);
}
H5G.close(datasetGroupId);
H5F.close(fileId);
}
And this is the OPC UA Class
public class OPC_UA
{
public DateTime timeStamp { get; set; }
public string data { get; set; }
public Int64 quality { get; set; }
public OPC_UA(DateTime? ts = null ,string dt = "",Int64 qlt = -99)
{
if (!ts.HasValue)
{
timeStamp = DateTime.Now;
}
data = dt;
quality = qlt;
}
public DateTime timeStamp { get; set; }
DateTime is not an atomic datatype. Convert it into long/Int64 ticks. Then you might be able to write it.
public DateTime timeStamp
{
get { return new DateTime(StartTimeTicks); }
set
{
StartTimeTicks = value.Ticks;
}
}
public long StartTimeTicks;
The constructor may also be hindering it.
The workaround would be making a structure. Note: Make the structure of fields or properties, depending upon (GetFields)fields or (GetProperties)properties you will to use in the reflection to read it.
public structure OPC_UA
{
public string data;
public Int64 quality ;
public DateTime timeStamp
{
get { return new DateTime(StartTimeTicks); }
set
{
StartTimeTicks = value.Ticks;
}
}
public long StartTimeTicks;
}
Hdf5DotnetTools is a working C# wrapper on Hdf.PInvoke. You can write a compound dataset by using a C# structure list.
If you have any difficulty in using the functions of either Hdf.PInvoke or Hdf5DotnetTools, you can use its UnitTest project.
today I am dealing with issue how to save byte array to SQLite database. When I am saving base[] to databse, it's looks like that property holding that array, but actually it is not save to database, when I restart application on device. Maybe I have to convert byte[] to base 64 before putting it to database? If yes, how I can do that? Also maybe there are another way save to sqlite database , without converting to base64?
This is my object class:
public class Unit
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public bool IsStarted { get; set; }
public byte[] CImage { get; set; }
}
Here I am selecting image from gallery and set to bindable value:
IGalleryImageService galleryService = Xamarin.Forms.DependencyService.Get<IGalleryImageService>();
galleryService.ImageSelected += (o, imageSourceEventArgs) =>
{
ActiveP.CImageBindable = imageSourceEventArgs.ImageSource;
MemoryStream st = new MemoryStream(imageSourceEventArgs.ImageSource);
ImageSource imgSource = ImageSource.FromStream(() => st);
(ActiveP.Page as PTemplate).CImage.Source = imgSource;
};
galleryService.SelectImage();
here is my other Unit class where I am updating my database or inserting objects to database:
private void UpdateObjectToDB()
{
PUnit pUinit = new PUnit()
{
Id = this.Id,
IsStarted = this.IsStarted,
CImage = this.CImage
};
App.database.Update(pUinit);
}
public int InsertObjectToDB()
{
PUnit pUnit = new PUnit()
{
IsStarted = this.IsStarted,
CCImage = this.CImage
};
return App.database.AddItem(pUnit);
}
Where I have to convert and how to implement that? Thank you for answers or suggestions.
Well, I solved issue by changing this class property to string type:
public class Unit
{
[PrimaryKey, AutoIncrement]
public string CImageBase64 { get; set; }
}
Then in other unit class I changed bindable data type:
public string CImageBindable
{
get
{
return base.CImageBase64;
}
set
{
base.CImageBase64 = value;
OnPropertyChanged(nameof(CImageBindable));
}
}
And here I converted to base 64
if (this.P.CImageBindable == null)
{
CImage.Source = "img.png";
}
else
{
var imageBytes = Convert.FromBase64String(this.P.CImageBindable);
MemoryStream st = new MemoryStream(imageBytes);
ImageSource imgSource = ImageSource.FromStream(() => st);
CImage.Source = imgSource;
}
And picture selection converted as well:
IGalleryImageService galleryService = Xamarin.Forms.DependencyService.Get<IGalleryImageService>();
galleryService.ImageSelected += (o, imageSourceEventArgs) =>
{
ActiveParking.CImageBindable = Convert.ToBase64String(imageSourceEventArgs.ImageSource);
MemoryStream st = new MemoryStream(imageSourceEventArgs.ImageSource);
ImageSource imgSource = ImageSource.FromStream(() => st);
(ActiveParking.Page as PTemplate).CImage.Source = imgSource;
};
galleryService.SelectImage();
You can try this
private void UpdateObjectToDB()
{
PUnit pUinit = new PUnit()
{
Id = this.Id,
IsStarted = this.IsStarted,
CImage = Encoding.ASCII.GetBytes(this.CImage)
};
App.database.Update(pUinit);
}
public int InsertObjectToDB()
{
PUnit pUnit = new PUnit()
{
IsStarted = this.IsStarted,
CCImage = Encoding.ASCII.GetBytes(this.CImage)
};
return App.database.AddItem(pUnit);
}
Here's the code.
[Serializable]
public class HostedGame
{
public int ID { get; set; }
public int UID { get; set; }
public String Name { get; set; }
public Boolean Available { get; set; }
public String Description { get; set; }
public List<int> Users { get; set; }
public int Port { get; set; }
public HostedGame(int uid, String name, String description, int port)
{
UID = uid;
Name = name;
Description = description;
Available = true;
Port = port;
Users = new List<int>();
}
public int CompareTo(Object obj)
{
int result = 1;
if(obj != null && obj is HostedGame)
{
HostedGame w = obj as HostedGame;
result = this.ID.CompareTo(w.ID);
}
return result;
}
static public int Compare(HostedGame x, HostedGame y)
{
int result = 1;
if(x != null && y != null)
{
result = x.CompareTo(y);
}
return result;
}
public static HostedGame DeSerialize(byte[] data)
{
MemoryStream ms = new MemoryStream(data);
BinaryFormatter bff = new BinaryFormatter();
return (HostedGame)bff.Deserialize(ms);
}
public static byte[] Serialize(HostedGame obj)
{
BinaryFormatter bff = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bff.Serialize(ms, obj);
return ms.ToArray();
}
}
The code bellow doesn't seem to work right:
HostedGame hs = new HostedGame(12,"Name", "Description", 8088);
String s = Encoding.ASCII.GetString(HostedGame.Serialize(hs));
HostedGame HACK = HostedGame.DeSerialize(Encoding.ASCII.GetBytes(s));
HACK.Port for some reason comes out being 7999?
When I just do this...
HostedGame HACK = HostedGame.DeSerialize(HostedGame.Serialize(hs));
It works fine.
So, what I'm asking is
Why am I getting a wrong value?
Is there a better way to convert the bytes to a string and back again?
You cannot use Encoding.ASCII.GetString to convert any byte array to a string. You are losing some data when you do this. Use Convert.ToBase64String instead. This one will make a string from any byte sequence without losing the data.
HostedGame hs = new HostedGame(12,"Name", "Description", 8088);
String s = Convert.ToBase64String(HostedGame.Serialize(hs));
HostedGame HACK= HostedGame.DeSerialize(Convert.FromBase64String(s));
Here is an example, that shows how using Encoding.ASCII loses the data.
var testBytes = new byte[] { 250, 251, 252 };
var text = Encoding.ASCII.GetString(testBytes);
var bytes = Encoding.ASCII.GetBytes(result); // will be 63, 63, 63
Binary serialization generates a byte array which is not (necessarily) a valid string in any encoding.
When you try to read it as ASCII text, the ASCII decoder will convert any invalid bytes (> 128) into ? characters.
Therefore, when you turn it back into ASCII bytes, you end up with a different set of bytes.
In short, don't treat binary data as ASCII, or as any other text encoding.
If you need to send binary data as plain text, use Base64 to safely convert it to text.