Custom class: constructor and heritage

erupter

Member
Joined
Jun 29, 2010
Messages
12
Programming Experience
1-3
Hello all.
I'm building a class, admittedly it's my first almost serious attempt.
Anyway, here it is
VB.NET:
Imports System.Timers
Imports System.Text
Imports GMap.NET

Public Class InertialModel
    Private _mass As Single
    Private _attitude As New axis3
    Private _speed As Single
    Private _heading As Single
    Private _position As PointLatLng
    Private _timestamp As Integer
    Private _name As String
    Private WithEvents _timer As Timer

    Public Property position As PointLatLng
        Get
            Return _position
        End Get
        Set(ByVal value As PointLatLng)
            _position = value
        End Set
    End Property

    Public Property mass() As Double
        Get
            Return _mass
        End Get
        Set(ByVal value As Double)
            _mass = value
        End Set
    End Property

    Public Property attitude() As axis3
        Get
            Return _attitude
        End Get
        Set(ByVal value As axis3)
            _attitude = value
        End Set
    End Property

    Public Property speed() As Single
        Get
            Return _speed
        End Get
        Set(ByVal value As Single)
            _speed = value
        End Set
    End Property

    Public Function GpsUpdate() As Boolean
        'read gps
        Return True
    End Function

    Public Function SensorUpdate() As Boolean
        'read sensors
        Return True
    End Function

    Public Sub New()
        Dim builder As New StringBuilder()
        Dim random As New Random()
        Dim ch As Char
        Dim i As Integer
        For i = 0 To 9
            ch = Convert.ToChar(Convert.ToInt32((26 * random.NextDouble() + 65)))
            builder.Append(ch)
        Next
        _name = builder.ToString
        _position.Lat = 41.8273388828218
        _position.Lng = 12.4710702896118
        _speed = 0
        _heading = 0
        _mass = 10
        _attitude = New axis3
        '_timer.AutoReset = True
        '_timer.Interval = 50
        '_timer.Start()
    End Sub

    Public Sub New(ByVal name As String, ByVal lat As Double, ByVal lng As Double, ByVal heading As Single, ByVal speed As Single, ByVal mass As Single)
        _name = name
        _position.Lat = lat
        _position.Lng = lng
        _heading = heading
        _speed = speed
        _mass = mass
    End Sub

    Private Sub _timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _timer.Elapsed

    End Sub

    Public Overrides Function ToString() As String
        Return String.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Name={0},Lat={1},Lng={2},speed={3},hdg={4}}}", _name, _position.Lat, _position.Lng, _speed, _heading)
    End Function

    Public Sub AttitudeSet(ByVal val1 As Single, ByVal val2 As Single, ByVal val3 As Single)
        _attitude.AngleX = val1
        _attitude.AngleY = val2
        _attitude.AngleZ = val3
    End Sub
End Class



Public Structure axis3

    Private _angleX As Single
    Private _angleY As Single
    Private _angleZ As Single

    Public Sub axis3(ByVal val1 As Single, ByVal val2 As Single, ByVal val3 As Single)
        _angleX = val1
        _angleY = val2
        _angleZ = val3
    End Sub


    Public Function IsEmpty() As Boolean
        Return ((_angleX And 0) And (_angleY And 0) And (_angleZ And 0))
    End Function

    Public Property AngleX As Single
        Get
            Return _angleX
        End Get
        Set(ByVal value As Single)
            _angleX = value
        End Set
    End Property

    Public Property AngleY As Single
        Get
            Return _angleY
        End Get
        Set(ByVal value As Single)
            _angleY = value
        End Set
    End Property

    Public Property AngleZ As Single
        Get
            Return _angleZ
        End Get
        Set(ByVal value As Single)
            _angleZ = value
        End Set
    End Property

    Public Sub Empty()
        _angleX = 0
        _angleY = 0
        _angleZ = 0
    End Sub
End Structure

And here are the problems
1)
if i declare a new object as axis3, like
VB.NET:
dim myobj as new axis3
then
VB.NET:
myobj.axis3(3,3,3)
works as expected, assigning the values at each of the member of the structure.
If i do the same for the InertialModel class, like
VB.NET:
dim myobj as new interialmodel
myobj.attitude.axis3(3,3,3)
it doesn't work. As you can see at the end of the class definition i had to add my own sub to set the values. The one inherited from the axis3 structure doesn't work. And i can't understand why.
Right now what i do is
VB.NET:
        Dim temp As New axis3
        temp.axis3(3, 3, 3)
        state.attitude = temp

2)axis3 constructor
how can i do something like
VB.NET:
 dim myobj as newtype = newtype(value1, value2)
???
Right now i can't figure a way to allow for inline declaration and assignment for the axis3 structure...

And finally a more general question: how could i go to send instances of my class through a socket? Or should i not use sockets?
 
Structure's are value types... because they are value types you are handed an actual value in a getter property. Modifying that value doesn't modify the value inside your instance of your class, because they aren't the same object.

note the error:

Expression is a value and therefore cannot be the target of an assignment



It's just like the Control.Location property... for any control you can't say:

myControl.Location.X = 15 ''<- results in same exact error




this doesn't happen if the property is handing back a object that is of some class type. Because all objects that are class types are also 'reference types'... you pass references of these objects around.
 
Ok thanks, so all it takes is converting from structure to class. perfect.

Now onto my second problem: how can i send an instance of my class to another application?

I have done this in ansi C: converting everything in a bytestream, with unusable characters and shifts, and predefined positions for data types.
But doing such a thing in vb.net would be a nightmare: direct byte control is cumbersome to put it mildly.
So how should i go at it? Aren't there facilities in the .NET framework to pass objects between applications?
 
how can i do something like
Code:
dim myobj as newtype = newtype(value1, value2)
Define a constructor with parameters, you already did that in InertialModel class.
how could i go to send instances of my class through a socket?
It is called serialization, and can be xml (XmlSerializer) or binary (BinaryFormatter). For binary you need to reference a common class library that define the type, for xml if I'm not mistaken you can get away with defining the 'same' type in both applications. Serialization can be done directly over the NetworkStream. Look it up and you should be able to find examples for these things.
 
Define a constructor with parameters, you already did that in InertialModel class.

It is called serialization, and can be xml (XmlSerializer) or binary (BinaryFormatter). For binary you need to reference a common class library that define the type, for xml if I'm not mistaken you can get away with defining the 'same' type in both applications. Serialization can be done directly over the NetworkStream. Look it up and you should be able to find examples for these things.

Ok i implemented it but sometimes it works sometimes not.
The code is pretty big to post up here, but here is the thing:
i set up a client-server socket, and am able to pass data. in fact i've been sending plain strings nicely. no errors.

Now here is what i did
VB.NET:
CLIENT
Private binform As New BinaryFormatter

'somehow i connect the tcpclient

'then in some cycle of mine
Dim networkStream As NetworkStream = tcpClient.GetStream()
binform.Serialize(networkStream, state)

then on the server side
VB.NET:
Drones(counter1)._state = binform.Deserialize(networkStream)

Now Drones is a class of mine which is composed of an InertialModel instance and a TcpClient instance, so in one LIST each object has all the properties it needs.
Now the code for the server is the one which screws up.
It decodes one every two messages sent.
I have my client showing up timestamps for each time it sends something (once per second), and the server shows timestamps too (every two seconds).
I can't understand why.
I used the same code i had for the strings, that worked well before.
Now it doesn't work at all. It cannot even separate different clients! (a thing it did well with strings).
Following is my code if you want to look at it

VB.NET:
Imports System.Data.SQLite
Imports System.IO
Imports System.Net
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Text
Imports System.Windows.Forms
Imports SWAN



Class MainWindow

    Dim Drones As IList(Of RemoteDrone) = New List(Of RemoteDrone)
    Dim LocalPort As Integer = 8000
    Dim LocalServer As New System.Net.Sockets.TcpListener(LocalPort)
    Dim temp As New System.Net.Sockets.TcpClient
    Dim DbConnection As New SQLiteConnection
    Dim DbCommand As New SQLiteCommand
    Dim LocalDbFile As String = System.IO.Path.GetTempPath + "locals.db3"
    Dim DbPassword As String
    Dim binform As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
    Dim WithEvents Timer1 As New System.Windows.Forms.Timer

    Public Sub New()
        Timer1.Interval = 50
        Timer1.Start()
        ' This call is required by the designer.
        InitializeComponent()
        LocalServer.Start()
        DatabaseInit()
        KnownClientsIndicator.Content = "0"

        'System.Globalization.NumberFormatInfo = ("en-US")
        ' Add any initialization after the InitializeComponent() call.


    End Sub

    Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        If Drones Is Nothing Then
            KnownClientsIndicator.Content = ("0")
        Else
            KnownClientsIndicator.Content = Drones.Count.ToString
        End If

        If LocalServer.Pending Then
            Drones.Add(New RemoteDrone)
            Drones(Drones.Count - 1)._tcpClient = LocalServer.AcceptTcpClient

            RichTextBox1.AppendText("New Client Connected: no " + Convert.ToString(Drones.Count - 1) + vbCrLf)
        End If



        If Drones.Count > 0 Then
            For counter1 As Integer = 0 To Drones.Count - 1
                If Drones(counter1)._tcpClient.Available > 0 Then
                    Dim networkStream As System.Net.Sockets.NetworkStream = Drones(counter1)._tcpClient.GetStream()
                    Dim bytes(Drones(counter1)._tcpClient.ReceiveBufferSize) As Byte
                    networkStream.Read(bytes, 0, CInt(Drones(counter1)._tcpClient.ReceiveBufferSize))
                    Dim clientdata As String = Encoding.ASCII.GetString(bytes)


                    If clientdata.Contains("closing") Then
                        Drones(counter1)._tcpClient.Close()
                        Drones.RemoveAt(counter1)
                        RichTextBox1.AppendText("Client no " + Convert.ToString(counter1) + " left." + vbCrLf)
                        RichTextBox1.ScrollToEnd()
                        Exit For
                    Else

                        Try
                            Drones(counter1)._state = binform.Deserialize(networkStream)
                            AddItemToDb(Drones(counter1)._state)
                            RichTextBox1.AppendText("Data received from client " + Convert.ToString(counter1) + " at: " + Now.TimeOfDay.ToString)
                            RichTextBox1.AppendText(vbCrLf)
                            RichTextBox1.ScrollToEnd()
                            For counter2 As Integer = 0 To Drones.Count - 1
                                If counter2 <> counter1 Then
                                    Dim networkStream2 As System.Net.Sockets.NetworkStream = Drones(counter2)._tcpClient.GetStream()
                                    Dim sendBytes As [Byte]() = Encoding.ASCII.GetBytes(clientdata)
                                    Try
                                        networkStream2.Write(sendBytes, 0, sendBytes.Length)
                                    Catch ex As Exception
                                        MsgBox(ex.Message)
                                    End Try

                                End If
                            Next
                        Catch ex As Exception
                        End Try

                    End If

                End If
            Next
        End If

    End Sub

    'Database Management
    Private Sub DatabaseInit()
        'If System.IO.File.Exists(LocalDbFile) Then
        '    System.IO.File.Delete(LocalDbFile)
        'End If
        'SQLiteConnection.CreateFile(LocalDbFile)
        DbConnection.ConnectionString = "Data Source=" + LocalDbFile + ";"
        'DbPassword = TimeOfDay
        'DbConnection.SetPassword(DbPassword)
        DbConnection.Open()
        DbCommand = DbConnection.CreateCommand
        'DbCommand.CommandText = "CREATE TABLE [knownclients] ([pk] INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,[id] TEXT  NULL,[lastseen] TIMESTAMP  NULL);"
        'DbCommand.ExecuteNonQuery()
        'DbCommand.CommandText = "CREATE TABLE [messages] ([pk] INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,[name] TEXT  NULL,[lat] FLOAT  NULL,[lng] FLOAT  NULL,[speed] FLOAT  NULL,[mass] FLOAT  NULL)"
        'DbCommand.ExecuteNonQuery()
        DbCommand.CommandText = "DELETE from messages"
        DbCommand.ExecuteNonQuery()
    End Sub

    Private Sub DatabaseDispose()
        DbConnection.Close()
        If System.IO.File.Exists(LocalDbFile) Then
            System.IO.File.Delete(LocalDbFile)
        End If

    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        RichTextBox1.Document.Blocks.Clear()
    End Sub

    Private Sub AddItemToDb(ByVal item As InertialModel)

        DbCommand.CommandText = String.Format(System.Globalization.CultureInfo.CreateSpecificCulture("en-US"), "INSERT INTO messages (name,lat,lng,speed,mass) values (" + ControlChars.Quote + "{0}" + ControlChars.Quote + ",{1},{2},{3},{4})", Drones(0)._state.Name, Drones(0)._state.Position.Lat, Drones(0)._state.Position.Lng, Drones(0)._state.Speed, Drones(0)._state.Mass)
        'MsgBox(DbCommand.CommandText)
        DbCommand.ExecuteNonQuery()

    End Sub


    Private Sub MainWindow_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Closing
        LocalServer.Stop()
        'DatabaseDispose()
    End Sub

End Class

It's a basic WPF project with two labels, one named "KnownClientsIndicator", a RichTextBox1 and a Button that clears the textbox.
Hope you can help me :(
 
For each 'drone' you start by reading some amount of bytes (receive buffer) with no regard to how much of that is string data and possibly part of the serialized object. One way around is to use BinaryWriter/Reader over the stream with WriteLine/ReadLine calls to transmit plain text messages. You do need to define a communication protocol in order to know when in code to read text data and when to read serialized data, else both will meddle.
 
That should not be a problem as the only case i am not sending a serialized object is when the client is quitting... anyway i'll try to go deeper on this.
 
Back
Top