I am trying to implement a C# listener for a Concox GPS tracker (model HVT001) using the code examples that are available online but for some reason I am not able to get the Terminal ID (IMEI) correctly and I can't get any location messages after the login
Below is a screen capture of the input from my console window:
As you can see the Terminal ID appears as gibberish and no location messages appear (I'm also getting protocol numbers that I don't recognize)
Any help would be appreciated. The full code is available here: https://docs.google.com/document/d/1UsF7ocb5CsCI1rxTcJHLP2eejR6vvboD_M8UeadkVmI/edit?usp=sharing
And here is a snippet of the code:
public void ProcessMessages()
{
UInt16 sendCRC = 0;
DateTime date;
int year = 0;
int month = 0;
int day = 0;
int hour = 0;
int minute = 0;
int second = 0;
KeyValuePair<List<byte>, StateObject> byteState;
KeyValuePair<UNPACK_STATUS, byte[]> status;
byte[] receiveMessage = null;
StateObject state = null;
byte[] serialNumber = null;
byte[] serverFlagBit = null;
byte[] stringArray = null;
string stringMessage = "";
byte lengthOfCommand = 0;
PROTOCOL_NUMBER protocolNumber = PROTOCOL_NUMBER.NONE;
try
{
Boolean firstMessage = true;
acceptDone.Set();
//loop forever
while (true)
{
allDone.WaitOne();
//read fifo until empty
while (true)
{
//read one connection until buffer doesn't contain any more packets
byteState = ReadWrite(PROCESS_STATE.PROCESS, null, null, -1);
if (byteState.Value.fifoCount == -1) break;
state = byteState.Value;
while (true)
{
status = Unpack(byteState);
if (status.Key == UNPACK_STATUS.NOT_ENOUGH_BYTES)
break;
if (status.Key == UNPACK_STATUS.ERROR)
{
Console.WriteLine("Error : Bad Receive Message, Data");
break;
}
//message is 2 start bytes + 1 byte (message length) + 1 byte message length + 2 end bytes
receiveMessage = status.Value;
int messageLength = receiveMessage[2];
Console.WriteLine("Status : '{0}', Receive Message : '{1}'", status.Key == UNPACK_STATUS.GOOD_MESSAGE ? "Good" : "Bad", BytesToString(receiveMessage.Take(messageLength + 5).ToArray()));
if (status.Key != UNPACK_STATUS.GOOD_MESSAGE)
{
break;
}
else
{
if (firstMessage)
{
if (receiveMessage[3] != 0x01)
{
Console.WriteLine("Error : Expected Login Message : '{0}'", BytesToString(receiveMessage));
break;
}
firstMessage = false;
}
//skip start bytes, message length. then go back 4 bytes (CRC and serial number)
serialNumber = receiveMessage.Skip(2 + 1 + messageLength - 4).Take(2).ToArray();
protocolNumber = (PROTOCOL_NUMBER)receiveMessage[3];
Console.WriteLine("Protocol Number : '{0}'", protocolNumber.ToString());
switch (protocolNumber)
{
case PROTOCOL_NUMBER.LOGIN_MESSAGE:
serialNumber.CopyTo(loginResponse, 4);
sendCRC = crc_bytes(loginResponse.Skip(2).Take(loginResponse.Length - 6).ToArray());
loginResponse[loginResponse.Length - 4] = (byte)((sendCRC >> 8) & 0xFF);
loginResponse[loginResponse.Length - 3] = (byte)((sendCRC) & 0xFF);
string IMEI = Encoding.ASCII.GetString(receiveMessage.Skip(4).Take(messageLength - 5).ToArray());
Console.WriteLine("Received good login message from Serial Number : '{0}', Terminal ID = '{1}'", "0x" + serialNumber[0].ToString("X2") + serialNumber[1].ToString("X2"), IMEI);
//byteState.Value.IMEI = IMEI;
Console.WriteLine("Send Message : '{0}'", BytesToString(loginResponse));
Send(state.workSocket, loginResponse);
WriteDBMessageLogin loginMessage = new WriteDBMessageLogin() { message = DATABASE_MESSAGE_TYPE.LOGIN, IMEI = IMEI, date = DateTime.Now };
WriteDBAsync.ReadWriteFifo(WriteDBAsync.Mode.WRITE, loginMessage);
Console.WriteLine("Wrote to database");
break;
case PROTOCOL_NUMBER.LOCATION_DATA:
year = receiveMessage[4];
month = receiveMessage[5];
day = receiveMessage[6];
hour = receiveMessage[7];
minute = receiveMessage[8];
second = receiveMessage[9];
date = new DateTime(2000 + year, month, day, hour, minute, second);
WriteDBMessageLocation locationMessage = new WriteDBMessageLocation();
locationMessage.message = DATABASE_MESSAGE_TYPE.LOCATION;
locationMessage.trackTime = date;
locationMessage.currTime = DateTime.Now;
locationMessage.lattitude = new byte[4];
Array.Copy(receiveMessage, 11, locationMessage.lattitude, 0, 4);
locationMessage.longitude = new byte[4];
Array.Copy(receiveMessage, 15, locationMessage.longitude, 0, 4);
locationMessage.speed = receiveMessage[19];
locationMessage.courseStatus = new byte[2];
Array.Copy(receiveMessage, 20, locationMessage.courseStatus, 0, 2);
locationMessage.IMEI = byteState.Value.IMEI;
WriteDBAsync.ReadWriteFifo(WriteDBAsync.Mode.WRITE, locationMessage);
Console.WriteLine("Received good location message from Serial Number '{0}', Time = '{1}'", "0x" + serialNumber[0].ToString("X2") + serialNumber[1].ToString("X2"), date.ToLongDateString());
break;
case PROTOCOL_NUMBER.ALARM_DATA:
//first response
int alarmPacketLen = alarmResponse.Length - 5;
alarmResponse[2] = (byte)(alarmPacketLen & 0xFF);
serialNumber.CopyTo(alarmResponse, alarmPacketLen - 1);
sendCRC = crc_bytes(alarmResponse.Skip(2).Take(alarmPacketLen - 1).ToArray());
alarmResponse[alarmPacketLen + 1] = (byte)((sendCRC >> 8) & 0xFF);
alarmResponse[alarmPacketLen + 2] = (byte)((sendCRC) & 0xFF);
Console.WriteLine("Send Alarm Response Message : '{0}'", BytesToString(alarmResponse));
Send(state.workSocket, alarmResponse);
//second response
year = receiveMessage[4];
month = receiveMessage[5];
day = receiveMessage[6];
hour = receiveMessage[7];
minute = receiveMessage[8];
second = receiveMessage[9];
date = new DateTime(2000 + year, month, day, hour, minute, second);
Console.WriteLine("Received good alarm message from Serial Number '{0}', Time = '{1}'", "0x" + serialNumber[0].ToString("X2") + serialNumber[1].ToString("X2"), date.ToLongDateString());
int alarmDataAddressPacketLen = alarmDataAddressResponse.Length - 5;
alarmDataAddressResponse[2] = (byte)(alarmDataAddressPacketLen & 0xFF);
serialNumber.CopyTo(alarmDataAddressResponse, alarmDataAddressPacketLen - 1);
sendCRC = crc_bytes(alarmDataAddressResponse.Skip(2).Take(alarmDataAddressPacketLen - 1).ToArray());
alarmDataAddressResponse[alarmDataAddressPacketLen + 1] = (byte)((sendCRC >> 8) & 0xFF);
alarmDataAddressResponse[alarmDataAddressPacketLen + 2] = (byte)((sendCRC) & 0xFF);
Console.WriteLine("Send Alarm Data Address Message : '{0}'", BytesToString(alarmDataAddressResponse));
Send(state.workSocket, alarmDataAddressResponse);
break;
case PROTOCOL_NUMBER.STATUS_INFO:
serialNumber.CopyTo(heartbeatResponse, 4);
byte info = receiveMessage[4];
byte voltage = receiveMessage[5];
byte GSMsignalStrength = receiveMessage[6];
UInt16 alarmLanguage = (UInt16)((receiveMessage[7] << 8) | receiveMessage[8]);
ALARM alarm = (ALARM)((info >> 3) & 0x07);
sendCRC = crc_bytes(heartbeatResponse.Skip(2).Take(heartbeatResponse.Length - 6).ToArray());
heartbeatResponse[heartbeatResponse.Length - 4] = (byte)((sendCRC >> 8) & 0xFF);
heartbeatResponse[heartbeatResponse.Length - 3] = (byte)((sendCRC) & 0xFF);
Console.WriteLine("Received good status message from Serial Number : '{0}', INFO : '0x{1}{2}{3}{4}'",
"0x" + serialNumber[0].ToString("X2") + serialNumber[1].ToString("X2"),
info.ToString("X2"), voltage.ToString("X2"), GSMsignalStrength.ToString("X2"),
alarmLanguage.ToString("X4"));
Console.WriteLine("Send Message : '{0}'", BytesToString(heartbeatResponse));
Send(state.workSocket, heartbeatResponse);
switch (alarm)
{
//reset cut off alarm
case ALARM.POWER_CUT_ALARM:
int connectOilAndElectricityPacketLen = connectOilAndEletricity.Length - 5;
serialNumber.CopyTo(connectOilAndEletricity, connectOilAndElectricityPacketLen - 1);
sendCRC = crc_bytes(connectOilAndEletricity.Skip(2).Take(connectOilAndEletricity.Length - 6).ToArray());
connectOilAndEletricity[connectOilAndEletricity.Length - 4] = (byte)((sendCRC >> 8) & 0xFF);
connectOilAndEletricity[connectOilAndEletricity.Length - 3] = (byte)((sendCRC) & 0xFF);
serverFlagBit = new byte[4];
Array.Copy(connectOilAndEletricity, 5, serverFlagBit, 0, 4);
lengthOfCommand = connectOilAndEletricity[4];
stringArray = new byte[lengthOfCommand - 4]; //do not include server flag bit
Array.Copy(connectOilAndEletricity, 9, stringArray, 0, lengthOfCommand - 4);
stringMessage = Encoding.ASCII.GetString(stringArray);
Console.WriteLine("Reset Oil and Electricity, Server Flag Bit : '{0}{1}{2}{3}', Message : '{4}'",
serverFlagBit[0].ToString("X2"),
serverFlagBit[1].ToString("X2"),
serverFlagBit[2].ToString("X2"),
serverFlagBit[3].ToString("X2"),
stringMessage);
Send(state.workSocket, connectOilAndEletricity);
break;
}
break;
case PROTOCOL_NUMBER.STRING_INFO:
lengthOfCommand = receiveMessage[4];
serverFlagBit = new byte[4];
Array.Copy(receiveMessage, 5, serverFlagBit, 0, 4);
stringArray = new byte[lengthOfCommand - 4]; //do not include server flag bit
Array.Copy(receiveMessage, 9, stringArray, 0, lengthOfCommand - 4);
stringMessage = Encoding.ASCII.GetString(stringArray);
Console.WriteLine("String Message, Server Flag Bit : '{0}{1}{2}{3}', Message : '{4}'",
serverFlagBit[0].ToString("X2"),
serverFlagBit[1].ToString("X2"),
serverFlagBit[2].ToString("X2"),
serverFlagBit[3].ToString("X2"),
stringMessage);
break;
} //end switch
}// End if
} //end while
}//end while fifo > 0
allDone.Reset();
}//end while true
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
First of all, a google docs is no way to share code bear that in mind for next questions. I cant really test your code, but from what i read in the documentacion of this GPS. I can tell you that your IMEI problem is that you are not desearializing it well.
According to the documentacion the Login Message Packet has 18 bytes, and its divided this way in order
Here is your line where you deserialize it:
string IMEI = Encoding.ASCII.GetString(receiveMessage.Skip(4).Take(messageLength - 5).ToArray());
To begin with there is no need to make a calculation on how many you should take, they will always be 8 so receiveMessage.Skip(4).Take(8).ToArray()
Then there is the problem with the encoding, you dont want to convert those numbers into their ascii char, you want only to take the numbers being writen as a hex string, removing the dashes that generates the method, and removing the leading zero if any exists
BitConverter.ToString(ByteArr).Replace("-","").TrimStart('0')
This is how your IMEI variable should look like:
string IMEI = BitConverter.ToString(receiveMessage.Skip(4).Take(8).ToArray()).Replace("-","").TrimStart('0');