In Photon Bolt, how to send List of int data in a token along with event? - photon

My codes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Bolt;
using UdpKit;
//using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class ListOfIntToken : IProtocolToken
{
public List<int> intList;
public int byteArraySize;
public void Read(UdpPacket packet)
{
byteArraySize = packet.ReadInt();
var objectBytes = packet.ReadByteArray(byteArraySize);
var mStream = new MemoryStream();
var binFormatter = new BinaryFormatter();
mStream.Write(objectBytes, 0, objectBytes.Length);
mStream.Position = 0;
intList = binFormatter.Deserialize(mStream) as List<int>;
}
public void Write(UdpPacket packet)
{
var binFormatter = new BinaryFormatter();
var mStream = new MemoryStream();
binFormatter.Serialize(mStream, intList);
//byte[] bytes = userId.Select(x => (byte)x).ToArray();
var byteArray = mStream.ToArray();
byteArraySize = byteArray.Length;
packet.WriteInt(byteArraySize);
packet.WriteByteArray(byteArray);
}
}
I have two clients running A and B. A is server. Both are sending the event with this token but with different data for testing. In Write method I print the byteArraySize out, and when the data are received on server I print them out too. The byteArraySize for A's data is 0, and the time it's printed is before the printing line inWrite method, where the size was 221. However for B's data the size was correct. What may causes this problem?

Final solution:
public void Read(UdpPacket packet)
{
intList.Clear();
if (packet.ReadBool()) // check if we have data to read
{
var total = packet.ReadInt();
for (int i = 0; i < total; i++)
{
intList.Add(packet.ReadInt());
}
}
}
public void Write(UdpPacket packet)
{
var total = intList.Count;
if (packet.WriteBool(total > 0)) // Write bool to signal we have some data
{
packet.WriteInt(total);
foreach (var item in intList)
{
packet.WriteInt(item);
}
}
}
The weird bug is caused by assigning to a wrong variable. It's been corrected in the question.

Related

ASP.NET Stored Procedure Call insert VarBinary into sql

I have a string that is data bytes base64EncodedString from iOS which is an extremely long string
let imageStr = imageData.base64EncodedString()
I am calling a .NET Method from my ios that will call a stored procedure to insert these bytes into the database.
Here is my .NET Method, I have the data type set to VarBinary
public string PostLandGradingImages(List<Images> landingCells)
{
try
{
using (connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("PostLandGradingImages", connection))
{
command.CommandType = CommandType.StoredProcedure;
for (int i = 0; i < landingCells.Count; i++)
{
command.Parameters.Clear();
SqlParameter parameter1 = new SqlParameter("#Job_No", SqlDbType.VarChar);
parameter1.Value = landingCells[i].jobNo;
parameter1.Direction = ParameterDirection.Input;
command.Parameters.Add(parameter1);
SqlParameter parameter2 = new SqlParameter("#Image", SqlDbType.VarBinary);
parameter2.Value = landingCells[i].imageBytes;
parameter2.Direction = ParameterDirection.Input;
command.Parameters.Add(parameter2);
command.ExecuteNonQuery();
}
}
}
}
catch (Exception e)
{
return e.Message.ToString();
}
return "All Good";
}
Here is my Image Class, notice my imageBytes is defined as a byte[]:
public class Images
{
public string jobNo { get; set; }
public byte[] imageBytes { get; set; }
}
The column I am inserting into is defined as varbinary(MAX)
and here is my stored procedure:
ALTER PROCEDURE [dbo].[PostLandGradingImages]
-- Add the parameters for the stored procedure here
#Job_No varchar(MAX) = NULL,
#Image varbinary(MAX) = NULL
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO LandGradingImages (Job_No, ImageBytes) VALUES (#Job_No, #Image)
END
My problem is nothing is getting inserted, I am getting this error in my catch:
Object reference not set to an instance of an object.
My question is, what am I doing wrong? Should I not be sending base64EncodedString or am I not setting my class right? or my db column?
I tried this:
byte[] bytes = System.Convert.FromBase64String(landingCells[i].imageBytes);
SqlParameter parameter2 = new SqlParameter("#Image", SqlDbType.VarBinary, 800000);
parameter2.Value = bytes;
parameter2.Direction = ParameterDirection.Input;
command.Parameters.Add(parameter2);
Still does not work :( and I changed imageBytes to string.
I modified your code a little to the method below. It creates a new CommandType.StoredProcedure for every Image. Also the results are returned per image, so you can see which ones failed. In your method, if you have 10 images, and the 9th failed, you would not know that.
public List<Images> PostLandGradingImages(List<Images> landingCells)
{
//create a connection to the database
using (SqlConnection connection = new SqlConnection(Common.connectionString))
{
//loop all the images
for (int i = 0; i < landingCells.Count; i++)
{
//create a fresh sql command for every Image
using (SqlCommand command = new SqlCommand("PostLandGradingImages", connection))
{
command.CommandType = CommandType.StoredProcedure;
//add the parameters
command.Parameters.Add("#Job_No", SqlDbType.VarChar).Value = landingCells[i].jobNo;
command.Parameters.Add("#Image", SqlDbType.VarBinary).Value = landingCells[i].imageBytes;
try
{
//open the connection if closed
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
//execute the stored procedure
command.ExecuteNonQuery();
//set the save result to the image
landingCells[i].saveResult = true;
}
catch (Exception ex)
{
//handle error per Image
landingCells[i].errorMessage = ex.Message;
}
}
}
}
return landingCells;
}
In order to track the save result per image I've added two properties to the Image class, but this can be done in various other ways as well.
public class Images
{
public string jobNo { get; set; }
public byte[] imageBytes { get; set; }
public bool saveResult { get; set; }
public string errorMessage { get; set; }
}
A simple test was done with the following code. None of them gave a NullReference Error. Even with both properties being null, a database entry was still made.
//create a new list with Images
List<Images> landingCells = new List<Images>();
//add some dummy data
landingCells.Add(new Images() { jobNo = null, imageBytes = null });
landingCells.Add(new Images() { jobNo = "Job 1", imageBytes = null });
landingCells.Add(new Images() { jobNo = null, imageBytes = new byte[10000] });
landingCells.Add(new Images() { jobNo = "Job 2", imageBytes = new byte[10000] });
//send the images to be saved
landingCells = PostLandGradingImages(landingCells);
//loop all the images to check the result
for (int i = 0; i < landingCells.Count; i++)
{
if (landingCells[i].saveResult == false)
{
//display the result for each failed image
Label1.Text += landingCells[i].errorMessage + "<br>";
}
}
If there is still a NullReference error, that means that your List landingCells itself is null, or an Image object within that List is null (in which case it should never have been added to the List in the first place imho). You can change the snippet easily to check for that.
Consider batching the queries in a transaction. Also you should validate the values provided to the method to make sure that you can call the stored procedure correctly.
public int PostLandGradingImages(List<Images> landingCells) {
int count = 0;
using (var connection = new SqlConnection(connectionString)) {
connection.Open();
//Transaction to batch the actions.
using (var transaction = connection.BeginTransaction()) {
foreach (var image in landingCells) {
if (valid(image)) {//validate input properties.
try {
using (SqlCommand command = connection.CreateCommand()) {
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "PostLandGradingImages";
command.Parameters
.Add("#Job_No", SqlDbType.VarChar, image.jobNo.Length)
.Value = image.jobNo;
command.Parameters
.Add("#Image", SqlDbType.VarBinary, image.imageBytes.Length)
.Value = image.imageBytes;
count += command.ExecuteNonQuery();
}
} catch {
//TODO: Log error
}
}
}
if (landingCells.Count == count) {
transaction.Commit();
}
}
}
return count;
}
private bool valid(Images image) {
return image != null && String.IsNullOrWhiteSpace(image.jobNo)
&& image.imageBytes != null && image.imageBytes.Length > 0;
}

Get a specific TestSuite by Id using the TFS API

I am trying to get a specific TestSuite using the TFS API for a TestPlan.
The TestSuite could exist anywhere within a TestSuite hierarchy, so, of course I could write a recursive function. I want something more efficient however.
Is there a method I am missing, or maybe a query that I could write?
If you already know the testSuiteId things are quite straightforward. You only need to know the name of your TeamProject teamProjectName:
using System;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.TestManagement.Client;
namespace GetTestSuite
{
class Program
{
static void Main()
{
int testSuiteId = 555;
const string teamProjectName = "myTeamProjectName";
var tpc =
TfsTeamProjectCollectionFactory.GetTeamProjectCollection(
new Uri("http://tfsURI"));
var tstService = (ITestManagementService)tpc.GetService(typeof(ITestManagementService));
var tProject = tstService.GetTeamProject(teamProjectName);
var myTestSuite = tProject.TestSuites.Find(testSuiteId);
}
}
}
If you don't, you probably need to go for a solution similar to the one presented here (it's a S.Raiten post), where recursion does come into picture. Access to a testPlanId is assumed:
using System;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.TestManagement.Client;
namespace GetTestSuite
{
class Program
{
static void Main()
{
int testPlanId = 555;
const string teamProjectName = "myTeamProjectName";
var tpc =
TfsTeamProjectCollectionFactory.GetTeamProjectCollection(
new Uri("http://tfsURI"));
var tstService = (ITestManagementService)tpc.GetService(typeof(ITestManagementService));
var tProject = tstService.GetTeamProject(teamProjectName);
var myTestPlan = tProject.TestPlans.Find(testPlanId);
GetPlanSuites(myTestPlan.RootSuite.Entries);
}
public static void GetPlanSuites(ITestSuiteEntryCollection suites)
{
foreach (ITestSuiteEntry suiteEntry in suites)
{
Console.WriteLine(suiteEntry.Id);
var suite = suiteEntry.TestSuite as IStaticTestSuite;
if (suite != null)
{
if (suite.Entries.Count > 0)
GetPlanSuites(suite.Entries);
}
}
}
}
}

ActionScript3: how to initialize array length in object after it has been declared?

I'm using an AS3 class that is:
package {
public class PeopleInfo {
public var elements:int;
public var PeopleName:Array;
public var PeopleInt:Array;
public var PeopleDecimal:Array;
}
}
In another file I've got:
<?xml version="1.0" encoding="utf-8"?>
<s:Application
creationComplete="initApp()"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
public var ii:int;
public var dataWriteToDB:PeopleInfo = new PeopleInfo;
public function initApp():void
{
// Initialize data provider array.
var int_data:Array = new Array(10, 20, 30, 40, 50);
var decimal_data:Array = new Array(13.11, 23.34, 35.69, 43.29, 58.92);
var name:Array = new Array("Joe", "Karen", "Simon", "Greg", "Alice");
dataWriteToDB.elements = 5; // number of elements in array
for (ii = 0; ii < dataWriteToDB.elements; ii++)
{
dataWriteToDB.PeopleName[ii] = name[ii];
dataWriteToDB.PeopleInt[ii] = int_data[ii];
dataWriteToDB.PeopleDecimal[ii] = decimal_data[ii];
}
}
and so on...
I'm getting a run-time error: Error #1009: Cannot access a property of method of a null object reference referring to the for loop's first line dataWriteToDB.PeopleName since it is NULL.
I'm guessing the problem here is that while dataWriteToDB is declared initially, the array lengths for the arrays in PeopleInfo class have not been set yet. Or, not sure otherwise why it's NULL. Anyone know how to clear this up?
The arrays have not been initialized in the class. You should do it in the declaration:
package {
public class PeopleInfo {
public var elements:int;
public var PeopleName:Array = [];
public var PeopleInt:Array = [];
public var PeopleDecimal:Array = [];
}
}
Also consider using push to add elements to the array, to avoid accidentally accessing a non-existing index:
for (ii = 0; ii < dataWriteToDB.elements; ii++)
{
dataWriteToDB.PeopleName.push(name[ii]);
dataWriteToDB.PeopleInt.push(int_data[ii]);
dataWriteToDB.PeopleDecimal.push(decimal_data[ii]);
}

Custom container not drawing

I've been reading about Vala over the past couple of days and decided to dive into it and make some Clutter widgets along the way. I'm currently trying to draw a private actor from my custom actor subclass. Here is a simplified version of what I've got so far.
public class MyContainer : Clutter.Actor, Clutter.Container {
private Clutter.Group group;
public MyContainer() {
group = new Clutter.Group();
group.set_parent(this);
}
public void add_actor(Clutter.Actor actor) {
group.add_actor(actor);
actor.show();
set_size(group.width, group.height);
actor_added(actor);
queue_redraw();
}
public void foreach(Clutter.Callback callback) {
group.foreach(callback);
queue_redraw();
}
public override void get_preferred_height(
float for_width,
out float min_height_p,
out float natural_height_p) {
group.get_preferred_height(
for_width,
out min_height_p,
out natural_height_p);
}
public override void get_preferred_width(
float for_height,
out float min_width_p,
out float natural_width_p) {
group.get_preferred_width(
for_height,
out min_width_p,
out natural_width_p);
}
public override void paint() {
group.paint();
}
public void remove_actor(Clutter.Actor actor) {
group.remove_actor(actor);
set_size(group.width, group.height);
actor_removed(actor);
queue_redraw();
}
public void sort_depth_order() {
group.sort_depth_order();
queue_redraw();
}
}
int main(string [] args) {
// Start clutter.
var result = Clutter.init(ref args);
if (result != Clutter.InitError.SUCCESS) {
stderr.printf("Error: %s\n", result.to_string());
return 1;
}
var stage = Clutter.Stage.get_default();
// Build a MyCollection object.
var myc = new MyContainer();
myc.x = 100;
myc.y = 100;
var r1 = new Clutter.Rectangle();
r1.width = 50;
r1.height = 50;
r1.color = Clutter.Color.from_string("rgb(255, 0, 0)");
var t1 = new Clutter.Text();
t1.text = "The red square.";
t1.y = r1.height;
// Build a Group object similar to the previous.
var group = new Clutter.Group();
group.x = 300;
group.y = 100;
var r2 = new Clutter.Rectangle();
r2.width = 50;
r2.height = 50;
r2.color = Clutter.Color.from_string("rgb(255, 0, 0)");
var t2 = new Clutter.Text();
t2.text = "The red square.";
t2.y = r2.height;
// Display.
myc.add_actor(r1);
myc.add_actor(t1);
group.add_actor(r2);
group.add_actor(t2);
stage.add_actor(myc);
stage.add_actor(group);
stage.show_all();
Clutter.main();
return 0;
}
The example paints the group added directly to the stage, but not the group wrapped by the custom collection that is added to the stage. How can I get this to work and what is wrong with the above?
I've been working on ubuntu 11.10 with valac --pkg clutter-1.0 above_code_example.vala.
This answer is from buz on gnome.irc's #clutter room.
The problem is a missing override for the allocate function.

Advantage database throws an exception when attempting to delete a record with a like statement used in the where clause

The code below shows that a record is deleted when the sql statement is:
select * from test where qty between 50 and 59
but the sql statement:
select * from test where partno like 'PART/005%'
throws the exception:
Advantage.Data.Provider.AdsException: Error 5072: Action requires read-write access to the table
How can you reliably delete a record with a where clause applied?
Note: I'm using Advantage Database v9.10.1.9, VS2008, .Net Framework 3.5 and WinXP 32 bit
using System.IO;
using Advantage.Data.Provider;
using AdvantageClientEngine;
using NUnit.Framework;
namespace NetworkEidetics.Core.Tests.Dbf
{
[TestFixture]
public class AdvantageDatabaseTests
{
private const string DefaultConnectionString = #"data source={0};ServerType=local;TableType=ADS_CDX;LockMode=COMPATIBLE;TrimTrailingSpaces=TRUE;ShowDeleted=FALSE";
private const string TestFilesDirectory = "./TestFiles";
[SetUp]
public void Setup()
{
const string createSql = #"CREATE TABLE [{0}] (ITEM_NO char(4), PARTNO char(20), QTY numeric(6,0), QUOTE numeric(12,4)) ";
const string insertSql = #"INSERT INTO [{0}] (ITEM_NO, PARTNO, QTY, QUOTE) VALUES('{1}', '{2}', {3}, {4})";
const string filename = "test.dbf";
var connectionString = string.Format(DefaultConnectionString, TestFilesDirectory);
using (var connection = new AdsConnection(connectionString)) {
connection.Open();
using (var transaction = connection.BeginTransaction()) {
using (var command = connection.CreateCommand()) {
command.CommandText = string.Format(createSql, filename);
command.Transaction = transaction;
command.ExecuteNonQuery();
}
transaction.Commit();
}
using (var transaction = connection.BeginTransaction()) {
for (var i = 0; i < 1000; ++i) {
using (var command = connection.CreateCommand()) {
var itemNo = string.Format("{0}", i);
var partNumber = string.Format("PART/{0:d4}", i);
var quantity = i;
var quote = i * 10;
command.CommandText = string.Format(insertSql, filename, itemNo, partNumber, quantity, quote);
command.Transaction = transaction;
command.ExecuteNonQuery();
}
}
transaction.Commit();
}
connection.Close();
}
}
[TearDown]
public void TearDown()
{
File.Delete("./TestFiles/test.dbf");
}
[Test]
public void CanDeleteRecord()
{
const string sqlStatement = #"select * from test";
Assert.AreEqual(1000, GetRecordCount(sqlStatement));
DeleteRecord(sqlStatement, 3);
Assert.AreEqual(999, GetRecordCount(sqlStatement));
}
[Test]
public void CanDeleteRecordBetween()
{
const string sqlStatement = #"select * from test where qty between 50 and 59";
Assert.AreEqual(10, GetRecordCount(sqlStatement));
DeleteRecord(sqlStatement, 3);
Assert.AreEqual(9, GetRecordCount(sqlStatement));
}
[Test]
public void CanDeleteRecordWithLike()
{
const string sqlStatement = #"select * from test where partno like 'PART/005%'";
Assert.AreEqual(10, GetRecordCount(sqlStatement));
DeleteRecord(sqlStatement, 3);
Assert.AreEqual(9, GetRecordCount(sqlStatement));
}
public int GetRecordCount(string sqlStatement)
{
var connectionString = string.Format(DefaultConnectionString, TestFilesDirectory);
using (var connection = new AdsConnection(connectionString)) {
connection.Open();
using (var command = connection.CreateCommand()) {
command.CommandText = sqlStatement;
var reader = command.ExecuteExtendedReader();
return reader.GetRecordCount(AdsExtendedReader.FilterOption.RespectFilters);
}
}
}
public void DeleteRecord(string sqlStatement, int rowIndex)
{
var connectionString = string.Format(DefaultConnectionString, TestFilesDirectory);
using (var connection = new AdsConnection(connectionString)) {
connection.Open();
using (var command = connection.CreateCommand()) {
command.CommandText = sqlStatement;
var reader = command.ExecuteExtendedReader();
reader.GotoBOF();
reader.Read();
if (rowIndex != 0) {
ACE.AdsSkip(reader.AdsActiveHandle, rowIndex);
}
reader.DeleteRecord();
}
connection.Close();
}
}
}
}
LIKE results in a static cursor instead of a live cursor, meaning it is a read-only dataset. To remove a row in this situation it would be better to use an SQL DELETE statement.
DELETE FROM test where partno LIKE 'PART/005%'
I'm assuming your tests are just that, only tests. They are using some fairly inefficient mechanisms to locate and remove rows.
Update after comment that there is no key field:
How about using the LEFT scalar instead of LIKE (might not work for all cases, but does for your example). If the size is always the same you could also add an index on left(partno,8) to increase the performance:
select * from test where left(partno,8) = 'PART/005'
Then you could use the Delete function of the extended data reader directly on this live result set (no gotop and skip).
Update after Alex's ROWID comment
I didn't know our ROWID came from the base table, even in static cursors. Alex's comment is the solution to your problem. First:
SELECT t.*, t.rowid FROM test t WHERE x LIKE 'PART/005%'
then:
DELETE FROM test WHERE rowid = :thisid

Resources