Untested port of the Ishtar protocol, actually not much of a port since there was really not much to change. http://kisvm2.epfl.ch/record/125676/files/emav08-ishtar-final.pdf

ishtar.cpp

Committer:
ssozonoff
Date:
2011-05-12
Revision:
0:086ea145b6d7

File content as of revision 0:086ea145b6d7:

/*
	Ishtar Embedded
	Copyright (C) 2008-2010:

		Alexandre Habersaat <alexandre dot habersaat at gmail dot com>
		Antoine Beyeler <abeyeler at ab-ware dot com>
			(http://www.ab-ware.com)
		Stephane Magnenat <stephane at magnenat dot net>
			(http://stephane.magnenat.net)
		
	All rights reserved.

	Ishtar Embedded is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Ishtar Embedded is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Ishtar Embedded. If not, see <http://www.gnu.org/licenses/>.
*/

/*!
*	\file ishtar.c
*	\brief Main file of the Ishtar Embedded server
*
*	\author Alexandre Habersaat, Antoine Beyeler
*
*	This file contains the core of the Ishtar Embedded server
*/

#include "ishtar.h"

#ifdef __cplusplus
extern "C" {
#endif

// Macro for FAR directive
#if defined(__dsPIC33F__) || defined(__PIC24F__)
	#define ISHTAR_ATTRIBUTE_FAR	__attribute__((far))
#else
	#define ISHTAR_ATTRIBUTE_FAR
#endif


//! The reception buffer of the server
unsigned char ishtar_buffer[ISHTAR_BUFFER_SIZE] ISHTAR_ATTRIBUTE_FAR;

// Statistics
//! The number of overflows that occured in the reception buffer
unsigned long ishtar_numberOfOverflows = 0;
//! The number of bytes dropped in the state Header1
unsigned long ishtar_numberOfDropHeader1 = 0;
//! The number of bytes dropped in the state Header2
unsigned long ishtar_numberOfDropHeader2 = 0;
//! The number of bytes dropped in the state Header3
unsigned long ishtar_numberOfDropHeader3 = 0;
//! The number of bytes dropped in the state Header4
unsigned long ishtar_numberOfDropHeader4 = 0;
//! The number of messages dropped after a size checksum error
unsigned long ishtar_numberOfDropSize = 0;
//! The number of messages dropped after a content checksum error
unsigned long ishtar_numberOfDropContent = 0;
//! The total number of bytes received
unsigned long ishtar_numberOfBytesReceived = 0;
//! The total number of bytes sent
unsigned long ishtar_numberOfBytesSent = 0;
//! The total number of bytes read from memory
unsigned long ishtar_numberOfBytesReadFromMemory = 0;
//! The total number of bytes written to memory
unsigned long ishtar_numberOfBytesWrittenToMemory = 0;
//! The number of messages that were valid
unsigned long ishtar_numberOfValidMessages = 0;
//! The number of messages that were valid but contained invalid data (client error or wrong checksum validated)
unsigned long ishtar_corruptedValidated = 0;
//! The snapshot size
unsigned long ishtar_snapshotSize = 0;
//! If an error occured with the non-volatile memory
unsigned char ishtar_memoryError = NO_ERROR;

//! The number of services / variables registered
ishtar_ServiceId ishtar_numberOfVariables = 0;
//! The registry of the services / variables
ishtar_ServiceDescription ishtar_services[ISHTAR_MAX_NUMBER_OF_SERVICES] ISHTAR_ATTRIBUTE_FAR;

//! The Id of the active snapshot (or the last one if none is active)
ishtar_RequestId ishtar_snapshotId = 0;
//! Snapshot active
unsigned char ishtar_snapshotActive = 0;


// read buffer
//! The start of the reception buffer
unsigned char * ishtar_bufBegin;
//! The end of the reception buffer
unsigned char * ishtar_bufEnd;
//! The position to write in the buffer for the next byte that is received
unsigned char * ishtar_bufPos;

//! The position of the next char to be consumed.
unsigned char * ishtar_consumePos;
//! Begining of the data payload of the current packet
unsigned char* ishtar_dataStartMarker;
//! The start of the first packet waiting to be treated in the buffer
unsigned char * ishtar_packetBegin = 0;

//! The state to read the first byte of message header
#define ISHTAR_STATE_HEADER1 1
//! The state to read the second byte of message header
#define ISHTAR_STATE_HEADER2 2
//! The state to read the third byte of message header
#define ISHTAR_STATE_HEADER3 3
//! The state to read the fourth byte of message header
#define ISHTAR_STATE_HEADER4 4
//! The state to read the size of message
#define ISHTAR_STATE_SIZE 5
//! The state to read the checksum of the size of the message
#define ISHTAR_STATE_CHECKSUM_SIZE 6
//! The state to read the content of the message
#define ISHTAR_STATE_CONTENT 7
//! The state to read the checksum of the content of the message
#define ISHTAR_STATE_CHECKSUM_CONTENT 8

//! The current state of the state machine for reception
unsigned char ishtar_state = ISHTAR_STATE_HEADER1;
//! Detects overflows in the reception buffer
unsigned char ishtar_overFlow = 0;
//! The current byte of the size being read
unsigned char ishtar_sizeByteNum = 0;
//! The current bypte of the checksum being read
unsigned char ishtar_checksumByteNum = 0;

// Checksum read
//! The checksum read for the size
unsigned short ishtar_sizeChecksumRead = 0;
//! The ckecksum read for the content
unsigned short ishtar_contentChecksumRead = 0;
// Checksum calculated
//! Temporary variables to calculate checksums
unsigned char ishtar_ckA, ishtar_ckB;
//! The total size of the current packet
unsigned long ishtar_curPacketSize = 0;
//! The number of bytes read for the current packet
unsigned long ishtar_curSize = 0;

//! At least one client is connected to the server
unsigned char ishtar_connected = 0;

// Non-volatile memory access
//! The lowest memory address that Ishtar is allowed to read/write
unsigned short ishtar_memoryMinAddress = 0;
//! The highest memory address that Ishtar is allowed to read/write
unsigned short ishtar_memoryMaxAddress = 0;
//! The external function to read the non-volatile memory
ishtar_MemoryReadFunc ishtar_memoryReadFunc = 0;
//! The external function to write the non-volatile memory
ishtar_MemoryWriteFunc ishtar_memoryWriteFunc = 0;
//! The checksum A for the non-volatile memory consistency
unsigned char ishtar_memoryChecksumA = 0;
//! The checksum B for the non-volatile memory consistency
unsigned char ishtar_memoryChecksumB = 0;

// Send function
//! The external function to call to send data through communication
ishtar_ExternalSendFunc ishtar_externalSendFunc;
//! The external function to call to send the first heavy messages (blocking function)
ishtar_ExternalSendFunc ishtar_externalSendBlockingFunc;
//! The current function that is used to send data (blocking or non-blocking)
ishtar_ExternalSendFunc ishtar_currentSendFunc;
//! The external function to flush the content of the external output buffer (if used)
ishtar_ExternalFlushFunc ishtar_externalFlushFunc;

//! The intermediate function to send data and calculate the checksum of the sent data
void ishtar_SendData(unsigned char * data, unsigned long len);
//! The function to send the message header
void ishtar_SendHeader();
//! The function to reset the checksums
void ishtar_InitCk();
//! The function to send the current checksum of the message
void ishtar_SendCk();

//! An empty function
void ishtar_DoNothing() { }

//! This function inits the pointers to the reception buffer
void ishtar_InitBuffer();
//! This function is used for debug, to register statistics variables into Ishtar
void ishtar_RegisterDebugVars();

// utils
//! This function is used to calculate the checksum of a size
unsigned short ishtar_CalculateSizeChecksum(unsigned long size);
//! This function normalizes an address (usually old address + offset) to stay inside the reception buffer
inline unsigned char * ishtar_WrapAddr(unsigned char * addr);
//! This function increments the address of the reception buffer
inline void ishtar_IncAddr(unsigned char ** addr, unsigned long size);

//! This function reads the content of an address and copies it into a variable (target)
inline void ishtar_At(unsigned char ** addr, unsigned char * target, unsigned char size);
// This function reads the content of an address, copies it into a variable and increment the address to the next position
inline void ishtar_AtInc(unsigned char ** addr, unsigned char * target, unsigned char size);

//! This function sends a zero-terminated string
void ishtar_SendString(const char * sz);
//! This function sends the value of a variable
void ishtar_SendValue(unsigned char * value, ishtar_ValueVectorType type, unsigned long index);
//! This function reads a value in a buffer, write it into the registry and increments the read buffer
void ishtar_SetValueInc(unsigned char * value, ishtar_ValueVectorType type, unsigned long index, unsigned char ** addr);

//! This function returns the length of a zero-terminated string or ISHTAR_STRING_MAX_SIZE
unsigned long ishtar_Strlen(const char * addr);
//! This function returns the size of a type
unsigned long ishtar_TypeSize(ishtar_ValueVectorType type);

// treat messages
//! This function decodes the type of a packet and call the corresponding dedicated function
void ishtar_HandlePacket(unsigned char * addr, unsigned long len);
//! This function decodes the content of a Hello packet and sends the answer
void ishtar_HandleHelloCall(unsigned char * addr, unsigned long len);
//! This function decodes the content of a GetServiceList packet and sends the answer
void ishtar_HandleGetServiceListCall(unsigned char * addr, unsigned long len);
//! This function decodes the content of a Get packet and sends the answer. It may also set up a snapshot.
void ishtar_HandleGetCall(unsigned char * addr, unsigned long len);
//! This function decodes the content of a Set packet and executes the modification on the local service registry
void ishtar_HandleSetCall(unsigned char * addr, unsigned long len);
//! This function decodes the content of a disconnection packet and stops the snapshot
void ishtar_HandleByeByeCall(unsigned char * addr, unsigned long len);

/*!
*	This function normalizes an address (usually old address + offset) to stay inside the reception buffer
*/
inline unsigned char * ishtar_WrapAddr(unsigned char * addr)
{
	if (addr >= ishtar_bufEnd)
	{
		return (unsigned char*)((unsigned int)ishtar_bufBegin + (unsigned int)addr - (unsigned int)ishtar_bufEnd);
	}
	else
	{
		return addr;
	}
}

/*!
*	This function increments the address of the reception buffer
*/
inline void ishtar_IncAddr(unsigned char ** addr, unsigned long size)
{
	*addr += size;
	*addr = ishtar_WrapAddr(*addr);
}

/*!
*	This function reads the content of an address and copies it into a variable (target)
*/
inline void ishtar_At(unsigned char ** addr, unsigned char * target, unsigned char size)
{
	unsigned char i;

	for (i = 0; i < size; ++i)
	{
		target[i] = *ishtar_WrapAddr(*addr + i);
	}
}

/*!
*	This function reads the content of an address, copies it into a variable and increment the address to the next position
*/
inline void ishtar_AtInc(unsigned char ** addr, unsigned char * target, unsigned char size)
{
	ishtar_At(addr, target, size);
	ishtar_IncAddr(addr, size);
}



/*!
*	This function sends a zero-terminated string
*/
void ishtar_SendString(const char * sz)
{
	// 4 name.type
	ishtar_ValueVectorSize nameSize;
	unsigned long j;

	// 4 name.size
	nameSize = ishtar_Strlen(sz);
	ishtar_SendData((unsigned char*)(&nameSize), sizeof(ishtar_ValueVectorSize));
	// X name
	for (j = 0; j < nameSize; ++j)
	{
		ishtar_SendData((unsigned char*)&(sz[j]), 1);
	}
}

/*!
*	This function sends the value of an element of a local variable
*
*	@param value The pointer to the variable to read
*	@param type The type of the variable
*	@param index The index of the element to read
*/
void ishtar_SendValue(unsigned char * value, ishtar_ValueVectorType type, unsigned long index)
{
	unsigned long size;
	unsigned char * val;

	size = ishtar_TypeSize(type);
	val = (unsigned char*) value;
	ishtar_SendData(&val[size * index], size);
}

/*!
*	This function modifies the value of one element of a local variable
*
*	@param value The pointer to the variable to set
*	@param type The type of the variable to set
*	@param index The index of the element to set (or 0 for a normal variable)
*	@param addr The address from which to read the value to set
*/
void ishtar_SetValueInc(unsigned char * value, ishtar_ValueVectorType type, unsigned long index, unsigned char ** addr)
{
	unsigned long i;
	unsigned long size;

	size = ishtar_TypeSize(type);

	for (i = 0; i < size; ++i)
	{
		value[size*index+i] = **addr;
		*addr = ishtar_WrapAddr(*addr + 1);
	}
}

/*!
*	This function calculates the checksum of a 4-bytes size
*
*	@return The size on which the checksum is calculated
*	@return The calculated checksum
*/
unsigned short ishtar_CalculateSizeChecksum(unsigned long size)
{
	unsigned char i;
	unsigned char * p;
	unsigned char ckA, ckB;

	ckA = 0;
	ckB = 0;
	p = (unsigned char*)(&size);
	for (i = 0; i < 4; ++i)
	{
		ckA += *(p + i);
		ckB += ckA;
	}

	return (ckA << 8 | ckB);
}

/*!
*	This function sends any data. The data sent are added to the current checksums
*
*	@param data A pointer to the data to send
*	@param len The number of bytes to send from the pointer
*/
void ishtar_SendData(unsigned char * data, unsigned long len)
{
	// Add checksums
	unsigned long i;
	for (i = 0; i < len; ++i)
	{
		ishtar_ckA += data[i];
		ishtar_ckB += ishtar_ckA;
	}
	// send data
	ishtar_currentSendFunc(data, len);

	ishtar_numberOfBytesSent += len;
}

/*!
*	This function sends the Ishtar headers
*/
void ishtar_SendHeader()
{
	// unsigned long header;
	unsigned char h1, h2, h3, h4;
	h1 = ISHTAR_HEADER1;
	h2 = ISHTAR_HEADER2;
	h3 = ISHTAR_HEADER3;
	h4 = ISHTAR_HEADER4;

	//header = (ISHTAR_HEADER1) | (ISHTAR_HEADER2 << 8) | (ISHTAR_HEADER3 << 16) | (ISHTAR_HEADER4 << 24);
	ishtar_currentSendFunc(&h1, 1);
	ishtar_currentSendFunc(&h2, 1);
	ishtar_currentSendFunc(&h3, 1);
	ishtar_currentSendFunc(&h4, 1);
}

/*!
*	This function resets the current checksum
*/
void ishtar_InitCk()
{
	// initialise checksums to 0
	ishtar_ckA = 0;
	ishtar_ckB = 0;
}

/*!
*	This function sends the current checksum
*/
void ishtar_SendCk()
{
	// compile actual checksums
	unsigned short checksum;
	checksum = (ishtar_ckA << 8) | ishtar_ckB;

	// send the compiled checksum
	ishtar_currentSendFunc((unsigned char*)(&checksum), 2);
}

/*!
*	This function is provided for convenience if multiple bytes are received at once.
*	It will call ishtar_ReceiveDataByte() for each of these data bytes
*/
void ishtar_ReceiveData(unsigned char * data, unsigned long len)
{
	unsigned long i;
	for (i = 0; i < len; ++i)
	{
		ishtar_ReceiveDataByte(data[i]);
	}
}

/*!
*	This function buffers all incoming bytes without processing them. It drop
*	any byte in case of buffer overflow. 
*/
void ishtar_ReceiveDataByte(unsigned char c)
{
	++ishtar_numberOfBytesReceived;
	
	unsigned char* nextAddr = ishtar_WrapAddr(ishtar_bufPos + 1);
	if (nextAddr == ishtar_packetBegin)
	{
		// we have an overflow
		ishtar_overFlow = 1;
		ishtar_numberOfOverflows++;
	}
	else
	{
		*ishtar_bufPos = c;
		ishtar_bufPos = nextAddr;
	}
}


/*!
*	This function process bytes from the buffer with a state machine. When a packet
*	is completed, it calls #ishtar_HandlePacket().
*
*	@param addr The address of the byte to process.
*/
void ishtar_ConsumeBufferedByte(unsigned char* addr)
{
	unsigned char * p;
	unsigned long i;
	unsigned char ckA, ckB;
	
	unsigned char c = *addr;
	

	if (ishtar_state == ISHTAR_STATE_CONTENT)
	{
		if (ishtar_curSize == 0)
			ishtar_dataStartMarker = addr;
			
		// Increase size
		++ishtar_curSize;

		// end of packet
		if (ishtar_curSize == ishtar_curPacketSize)
		{
			ishtar_contentChecksumRead = 0;
			ishtar_checksumByteNum = 0;
			ishtar_state = ISHTAR_STATE_CHECKSUM_CONTENT;
		}
	}
	else if (ishtar_state == ISHTAR_STATE_SIZE)
	{
		p = (unsigned char*)(&ishtar_curPacketSize);
		*( p + ishtar_sizeByteNum++ ) = c;
		
		if (ishtar_sizeByteNum == ISHTAR_NUMBER_OF_SIZE_BYTES)
		{
			ishtar_sizeChecksumRead = 0;
			ishtar_checksumByteNum = 0;
			ishtar_state = ISHTAR_STATE_CHECKSUM_SIZE;
		}
	}
	else if (ishtar_state == ISHTAR_STATE_HEADER1)
	{
		if (c == ISHTAR_HEADER1)
			ishtar_state = ISHTAR_STATE_HEADER2;
		else
			++ishtar_numberOfDropHeader1;
	}
	else if (ishtar_state == ISHTAR_STATE_CHECKSUM_SIZE)
	{
		p = (unsigned char*)(&ishtar_sizeChecksumRead);
		*(p + ishtar_checksumByteNum++) = c;

		if (ishtar_checksumByteNum == ISHTAR_NUMBER_OF_CHECKSUM_BYTES)
		{
			p = (unsigned char*)(&ishtar_curPacketSize);

			// calculate checksum of size
			ckA = 0;
			ckB = 0;
			for (i = 0; i < ISHTAR_NUMBER_OF_SIZE_BYTES; ++i)
			{
				ckA += (ishtar_curPacketSize >> (8 * i)) & 0xFF;
				ckB += ckA;
			}

			if (ishtar_sizeChecksumRead == (ckA << 8 | ckB))
			{
				ishtar_curSize = 0;
				ishtar_state = ISHTAR_STATE_CONTENT;
			}
			else
			{
				++ishtar_numberOfDropSize;
				ishtar_state = ISHTAR_STATE_HEADER1;
			}
		}
	}
	else if (ishtar_state == ISHTAR_STATE_CHECKSUM_CONTENT)
	{
		p = (unsigned char*)(&ishtar_contentChecksumRead);
		*(p + ishtar_checksumByteNum++) = c;

		if (ishtar_checksumByteNum == ISHTAR_NUMBER_OF_CHECKSUM_BYTES)
		{
			// calculate checksum of content, start after the size
			ckA = 0;
			ckB = 0;
			for (i = 0; i < ishtar_curPacketSize; ++i)
			{
				ckA += *(ishtar_WrapAddr(ishtar_dataStartMarker + i));
				ckB += ckA;
			}

			// checksum correct
			if (ishtar_contentChecksumRead == (ckA << 8 | ckB))
			{
				// handle the packet
				ishtar_HandlePacket(ishtar_dataStartMarker, ishtar_curPacketSize);				
				++ishtar_numberOfValidMessages;
			}
			else
				++ishtar_numberOfDropContent;
			
			// the begining of the buffer is at the next char in all cases.
			ishtar_packetBegin = ishtar_WrapAddr(addr + 1);

			// go back for next message
			ishtar_state = ISHTAR_STATE_HEADER1;
		}
	}
	else if (ishtar_state == ISHTAR_STATE_HEADER2)
	{
		if (c == ISHTAR_HEADER2)
			ishtar_state = ISHTAR_STATE_HEADER3;
		else if (c == ISHTAR_STATE_HEADER1)
		{
			ishtar_state = ISHTAR_STATE_HEADER2;
			++ishtar_numberOfDropHeader2;
		}
		else
		{
			ishtar_state = ISHTAR_STATE_HEADER1;
			++ishtar_numberOfDropHeader2;
		}
	}
	else if (ishtar_state == ISHTAR_STATE_HEADER3)
	{
		if (c == ISHTAR_HEADER3)
			ishtar_state = ISHTAR_STATE_HEADER4;
		else if (c == ISHTAR_STATE_HEADER1)
		{
			ishtar_state = ISHTAR_STATE_HEADER2;
			++ishtar_numberOfDropHeader3;
		}
		else
		{
			ishtar_state = ISHTAR_STATE_HEADER1;
			++ishtar_numberOfDropHeader3;
		}
	}
	else if (ishtar_state == ISHTAR_STATE_HEADER4)
	{
		if (c == ISHTAR_HEADER4)
		{
			ishtar_state = ISHTAR_STATE_SIZE;
			ishtar_curPacketSize = 0;
			ishtar_sizeByteNum = 0;
		}
		else if (c == ISHTAR_STATE_HEADER1)
		{
			ishtar_state = ISHTAR_STATE_HEADER2;
			++ishtar_numberOfDropHeader4;
		}
		else
		{
			ishtar_state = ISHTAR_STATE_HEADER1;
			++ishtar_numberOfDropHeader4;
		}
	}
	else
		ishtar_state = ISHTAR_STATE_HEADER1;
}

// step

/*!
*	This function checks if there are some packets to treat in the input buffer
*	If yes, it calls the decoding functions which send the answers to these packets.
*/
void ishtar_Step()
{
	// atomically read the curent bufPos
	unsigned char* bufPos = ishtar_bufPos;
	
	while (ishtar_consumePos != bufPos)
	{
		ishtar_ConsumeBufferedByte(ishtar_consumePos);
		ishtar_consumePos = ishtar_WrapAddr(ishtar_consumePos + 1);
	}
}

/*!
*	This function reads the start of a packet and determines it type.
*	It then calls the corresponding dedicated function to decode the packet.
*/
void ishtar_HandlePacket(unsigned char * addr, unsigned long len)
{
	ishtar_MessageId type;

	ishtar_AtInc(&addr, (unsigned char*)&type, sizeof(ishtar_MessageId));

	switch (type)
	{
		case HELLO:
		{
			ishtar_HandleHelloCall(addr, len - sizeof(ishtar_MessageId));
			break;
		}
		case GET_SERVICE_LIST:
		{
			ishtar_HandleGetServiceListCall(addr, len - sizeof(ishtar_MessageId));
			break;
		}
		case GET_VALUES:
		{
			ishtar_HandleGetCall(addr, len - sizeof(ishtar_MessageId));
			break;
		}
		case SET_VALUES:
		{
			ishtar_HandleSetCall(addr, len - sizeof(ishtar_MessageId));
			break;
		}
		case BYE_BYE:
		{
			ishtar_HandleByeByeCall(addr, len - sizeof(ishtar_MessageId));
			break;
		}
		default:
		{
			return;
		}
	}
}

/*!
*	This function decodes the Hello calls and sends the Hello answer
*/
void ishtar_HandleHelloCall(unsigned char * addr, unsigned long len)
{

	// Stop snapshot when hello received
	ishtar_snapshotActive = 0;

	ishtar_NodeType nodeType;
	ishtar_VersionType version;

	ishtar_MessageId hello = HELLO;
	ishtar_ProtocolType protocol;

	unsigned long answerSize;
	unsigned short sizeChecksum;

	// READ
	ishtar_AtInc(&addr, (unsigned char*)&nodeType, sizeof(ishtar_NodeType));
	ishtar_AtInc(&addr, (unsigned char*)&version, sizeof(ishtar_VersionType));

	// CALCULATE
	if (version == ISHTAR_PROTOCOL_VERSION)
		protocol = EMBEDDED;
	else
		protocol = INCOMPATIBLE;

	// SEND
	answerSize = sizeof(ishtar_MessageId) + sizeof(ishtar_ProtocolType);
	sizeChecksum = ishtar_CalculateSizeChecksum(answerSize);

	ishtar_currentSendFunc = ishtar_externalSendBlockingFunc;
	ishtar_SendHeader();
	// send size and size checksum
	ishtar_SendData((unsigned char*)(&answerSize), sizeof(unsigned long));
	ishtar_SendData((unsigned char*)(&sizeChecksum), sizeof(unsigned short));
	// send content
	ishtar_InitCk();
	ishtar_SendData((unsigned char*)(&hello), sizeof(ishtar_MessageId));
	ishtar_SendData((unsigned char*)(&protocol), sizeof(ishtar_ProtocolType));
	ishtar_SendCk();

	ishtar_externalFlushFunc();
	ishtar_currentSendFunc = ishtar_externalSendFunc;

	ishtar_connected = 1;
}

/*!
*	This function decodes the ServiceList calls and sends the list of all registered variables
*/
void ishtar_HandleGetServiceListCall(unsigned char * addr, unsigned long len)
{
	// Nothing to read
	// Calculate size
	unsigned long answerSize; // AnswerType + numberOfVariables
	ishtar_ServiceId i;

	ishtar_MessageId serviceList;
	unsigned short sizeChecksum;

	// READ - nothing

	// SEND
	answerSize = sizeof(ishtar_MessageId) + sizeof(ishtar_ServiceId);
	serviceList = SERVICE_LIST;
	// ID (4) + name.size(4) + name(X) + type(4) + flags(4) + length(4)
	// Must be in a for loop as the name of services have different sizes
	for (i = 0; i < ishtar_numberOfVariables; ++i)
	{
		answerSize += sizeof(ishtar_ServiceId) + sizeof(ishtar_ValueVectorSize) + ishtar_Strlen(ishtar_services[i].name) + sizeof(ishtar_ValueVectorType) + sizeof(ishtar_ServiceFlags) + sizeof(ishtar_ValueVectorSize);
	}

	// calculate size checksum
	sizeChecksum = ishtar_CalculateSizeChecksum(answerSize);

	ishtar_currentSendFunc = ishtar_externalSendBlockingFunc;
	ishtar_SendHeader();
	// send size and size checksum
	ishtar_SendData((unsigned char*)(&answerSize), sizeof(unsigned long));
	ishtar_SendData((unsigned char*)(&sizeChecksum), sizeof(unsigned short));

	// send content
	ishtar_InitCk();
	ishtar_SendData((unsigned char*)(&serviceList), sizeof(ishtar_MessageId));
	ishtar_SendData((unsigned char*)(&ishtar_numberOfVariables), sizeof(ishtar_ServiceId));

	// fill table
	for (i = 0; i < ishtar_numberOfVariables; ++i)
	{
		// 4 service id
		ishtar_SendData((unsigned char*)(&i), sizeof(ishtar_ServiceId));
		// name.size(4) + name(X)
		ishtar_SendString(ishtar_services[i].name);
		// 4 service.type
		ishtar_SendData((unsigned char*)(&(ishtar_services[i].type)), sizeof(ishtar_ValueVectorType));
		// 4 service.flags
		ishtar_SendData((unsigned char*)(&(ishtar_services[i].flags)), sizeof(ishtar_ServiceFlags));
		// 4 service.length
		ishtar_SendData((unsigned char*)(&(ishtar_services[i].length)), sizeof(ishtar_ValueVectorSize));
	}

	ishtar_SendCk();

	ishtar_externalFlushFunc();
	ishtar_currentSendFunc = ishtar_externalSendFunc;
}

/*!
*	This function decodes the Get calls and sends the values for the requested data
*/
void ishtar_HandleGetCall(unsigned char * addr, unsigned long len)
{
	// Read
	ishtar_RequestId requestID;
	ishtar_ServiceId numberOfIds;
	ishtar_ValueVectorSize numberOfIndices;


	unsigned long answerSize;// VALUES(4) + requestID(4) + size(4)
	unsigned short sizeChecksum;
	ishtar_ServiceId i;
	ishtar_ValueVectorSize j;

	ishtar_ServiceId id;
	ishtar_ValueVectorSize indice;
	ishtar_ValueVectorSize length;
	unsigned char snapshot = 0;
	ishtar_MessageId values;
	unsigned char * ids;

	// READ
	ishtar_AtInc(&addr,(unsigned char*)&requestID, sizeof(ishtar_RequestId));
	ishtar_AtInc(&addr,(unsigned char*)&snapshot, sizeof(unsigned char));
	ishtar_AtInc(&addr,(unsigned char*)&numberOfIds, sizeof(ishtar_ServiceId));

	if (snapshot)
	{
		// saves the snapshot id
		ishtar_snapshotId = requestID;
		// snapshot canceled
		if (numberOfIds == 0)
		{
			ishtar_snapshotActive = 0;
		}
		// new snapshot asked
		else
		{
			ishtar_snapshotActive = 1;
		}

		// clear old snapshot
		for (i = 0; i < ishtar_numberOfVariables; ++i)
		{
			ishtar_services[i].inSnapshot = 0;
		}
	}

	ids = addr;

	// Size
	answerSize = sizeof(ishtar_MessageId) + sizeof(ishtar_RequestId);

	values = VALUES;

	for (i = 0; i<numberOfIds; ++i)
	{
		// read id of service
		ishtar_AtInc(&addr,(unsigned char*)&id, sizeof(ishtar_ServiceId));

		// Problem, a corrupted message has been validated
		if ( id >= ishtar_numberOfVariables)
		{
			++ishtar_corruptedValidated;
			return;
		}

		// if the variable is in the snapshot
		if (snapshot)
		{
			ishtar_services[id].inSnapshot = 1;
		}

		// Read the number of indices
		ishtar_AtInc(&addr,(unsigned char*)&numberOfIndices, sizeof(ishtar_ValueVectorSize));
		// Eat these indices, so that the next variable id is correct
		for (j = 0; j < numberOfIndices; ++j)
		{
			ishtar_AtInc(&addr,(unsigned char*)&indice, sizeof(ishtar_ValueVectorSize));
			// Problem, a corrupter message has been validated
			if (indice > ishtar_services[id].length)
			{
				++ishtar_corruptedValidated;
				return;
			}
		}

		// if the size is 0, it means variable size, so we must specify it.
		if (ishtar_services[id].length == 0 && ishtar_services[id].type == UCHAR)
		{
			answerSize += sizeof(ishtar_ValueVectorSize);// size of the size
			answerSize += ishtar_Strlen((char*)(ishtar_services[id].value)); // size of the data
		}
		else
		{
			// we only put the data in the next message
			// here the full variable
			if (numberOfIndices == 0)
			{
				answerSize += ((unsigned long)(ishtar_services[id].length)) * ishtar_TypeSize(ishtar_services[id].type);
			}
			// here only some values
			else
			{
				answerSize += ((unsigned long)(numberOfIndices)) * ishtar_TypeSize(ishtar_services[id].type);
			}
		}
	}

	// answer only if non-snapshot, otherwise it will be done in ishtar_Snapshot()
	if (!snapshot)
	{
		sizeChecksum = ishtar_CalculateSizeChecksum(answerSize);

		// SEND
		ishtar_SendHeader();
		// send size and size checksum
		ishtar_SendData((unsigned char*)(&answerSize), 4);
		ishtar_SendData((unsigned char*)(&sizeChecksum), 2);

		// send content
		ishtar_InitCk();
		ishtar_SendData((unsigned char*)(&values), sizeof(ishtar_MessageId));
		ishtar_SendData((unsigned char*)(&requestID), sizeof(ishtar_RequestId));

		for (i = 0; i < numberOfIds; ++i)
		{
			// service id
			ishtar_AtInc(&ids, (unsigned char*)&id, sizeof(ishtar_ServiceId));
			// number of indices
			ishtar_AtInc(&ids,(unsigned char*)&numberOfIndices, sizeof(ishtar_ValueVectorSize));

			// length == 0 mean variable size, ONLY for char or UCHAR, with ended caracter equal to '\0'
			if (ishtar_services[id].type == UCHAR && ishtar_services[id].length == 0)
			{
				length = ishtar_Strlen((char*)(ishtar_services[id].value));
				ishtar_SendData((unsigned char*)(&length), sizeof(ishtar_ValueVectorSize));

				for (j = 0; j < length; ++j)
				{
					ishtar_SendValue((unsigned char*)ishtar_services[id].value, ishtar_services[id].type, j);
				}

			}
			else
			{
				// read the indices
				if (numberOfIndices > 0)
				{
					for (j = 0; j < numberOfIndices; ++j)
					{
						ishtar_AtInc(&ids,(unsigned char*)&indice, sizeof(ishtar_ValueVectorSize));
						ishtar_SendValue((unsigned char*)ishtar_services[id].value, ishtar_services[id].type, indice);

					}
				}
				else
				{
					for (j = 0; j < ishtar_services[id].length; ++j)
					{

						ishtar_SendValue((unsigned char*)ishtar_services[id].value, ishtar_services[id].type, j);
					}
				}
			}
		}

		ishtar_SendCk();
		ishtar_externalFlushFunc();
	}
}

/*!
*	This function decodes the Set calls and updates local variables
*/
void ishtar_HandleSetCall(unsigned char * addr, unsigned long len)
{
	ishtar_RequestId requestID;
	unsigned char feedback;
	ishtar_ServiceId numberOfIds;
	ishtar_ValueVectorSize numberOfIndices;
	ishtar_ServiceId i;
	ishtar_ValueVectorSize j;
	ishtar_ServiceId id;
	ishtar_ValueVectorSize indice;
	unsigned char * indicesAddr;
	
	unsigned long answerSize;
	unsigned short sizeChecksum;
	ishtar_MessageId setAck;


	// READ
	// request id
	ishtar_AtInc(&addr, (unsigned char*)&requestID, sizeof(ishtar_RequestId));
	// feedback
	ishtar_AtInc(&addr, (unsigned char*)&feedback, 1);
	//numberOfIds
	ishtar_AtInc(&addr, (unsigned char*)&numberOfIds, sizeof(ishtar_ServiceId));

	for (i = 0; i < numberOfIds; ++i)
	{
		//id = ishtar_ulongAtInc(&addr);
		ishtar_AtInc(&addr, (unsigned char*)&id, sizeof(ishtar_ServiceId));

		// Problem, a corrupted message has been validated
		if (id > ishtar_numberOfVariables)
		{
			++ishtar_corruptedValidated;
			return;
		}

		// number of indices
		ishtar_AtInc(&addr, (unsigned char*)&numberOfIndices, sizeof(ishtar_ValueVectorSize));
		indicesAddr = addr;

		// readonly, so skip
		if (ishtar_services[id].flags & READ_ONLY)
		{
			if (numberOfIndices == 0)
			{
				// skip
				ishtar_IncAddr(&addr, ishtar_TypeSize(ishtar_services[id].type) * ((unsigned long)(ishtar_services[id].length)));// values
			}
			else
			{
				// skip
				ishtar_IncAddr(&addr, numberOfIndices * sizeof(ishtar_ValueVectorSize));// indices
				ishtar_IncAddr(&addr, ishtar_TypeSize(ishtar_services[id].type) * numberOfIndices);// values
			}
		}
		else
		{
			if (numberOfIndices == 0)
			{
				for (j = 0; j < ishtar_services[id].length; ++j)
					ishtar_SetValueInc((unsigned char*)ishtar_services[id].value,ishtar_services[id].type, j, &addr);
			}
			else
			{
				ishtar_IncAddr(&addr, numberOfIndices * sizeof(ishtar_ValueVectorSize));

				// now addr points to the first value, indicesAddr to the first index

				for (j = 0; j < numberOfIndices; ++j)
				{
					ishtar_AtInc(&indicesAddr, (unsigned char*)&indice, sizeof(ishtar_ValueVectorSize));
					// Problem, a corrupted message has been validated
					if (indice > ishtar_services[id].length)
					{
						++ishtar_corruptedValidated;
						return;
					}
					
					ishtar_SetValueInc((unsigned char*)ishtar_services[id].value, ishtar_services[id].type, (unsigned long)indice, &addr);
				}
			}
			
			// call feedback function if existing
			if (ishtar_services[id].feedbackFunc)
				ishtar_services[id].feedbackFunc(&ishtar_services[id]);
		}
	}
	// SEND - the ack if feedback enabled
	if (feedback)
	{
		setAck = SET_ACK;
		answerSize = sizeof(ishtar_MessageId) + sizeof(ishtar_RequestId);
		sizeChecksum = ishtar_CalculateSizeChecksum(answerSize);

		ishtar_SendHeader();
		// send size and size checksum
		ishtar_SendData((unsigned char*)(&answerSize), 4);
		ishtar_SendData((unsigned char*)(&sizeChecksum), 2);

		// send content
		ishtar_InitCk();
		ishtar_SendData((unsigned char*)(&setAck), sizeof(ishtar_MessageId));
		ishtar_SendData((unsigned char*)(&requestID), sizeof(ishtar_RequestId));
		ishtar_SendCk();

		ishtar_externalFlushFunc();
	}
}

/*!
*	This function decodes the content of a disconnection packet and stops the snapshot.
*/
void ishtar_HandleByeByeCall(unsigned char * addr, unsigned long len)
{
	ishtar_snapshotActive = 0;
	ishtar_connected = 0;
}

/*!
*	This function first checks if there is an active snapshot.
*	If yes, it sends the data for this snapshot.
*/
void ishtar_Snapshot()
{
	if (ishtar_snapshotActive && ishtar_connected)
	{
		ishtar_RequestId requestID;

		unsigned long answerSize;// VALUES(4) + requestID(4) + size(4)
		unsigned short sizeChecksum;
		ishtar_ServiceId i;
		ishtar_ValueVectorSize j;
		ishtar_ValueVectorSize length;

		ishtar_MessageId values;

		answerSize = sizeof(ishtar_MessageId) + sizeof(ishtar_RequestId);

		values = VALUES;
		requestID = ishtar_snapshotId;

		// CALCULATE SIZE
		for (i = 0; i < ishtar_numberOfVariables; ++i)
		{
			// if the variable is in the snapshot
			if (ishtar_services[i].inSnapshot == 1)
			{
				// if the size is 0, it mean variable, so we must specify it.
				if (ishtar_services[i].length == 0)
				{
					answerSize += sizeof(ishtar_ValueVectorSize);
					answerSize += ishtar_Strlen((char*)(ishtar_services[i].value));
				}
				else
				{
					// in snapshots, the full variable is sent, cannot save indices in embedded version
					answerSize += ((unsigned long)(ishtar_services[i].length)) * ishtar_TypeSize(ishtar_services[i].type);
				}
			}
		}

		ishtar_snapshotSize = answerSize;
		sizeChecksum = ishtar_CalculateSizeChecksum(answerSize);

		// SEND
		ishtar_SendHeader();
		// send size and size checksum
		ishtar_SendData((unsigned char*)(&answerSize), 4);
		ishtar_SendData((unsigned char*)(&sizeChecksum), 2);

		// send content
		ishtar_InitCk();
		ishtar_SendData((unsigned char*)(&values), sizeof(ishtar_MessageId));
		ishtar_SendData((unsigned char*)(&requestID), sizeof(ishtar_RequestId));

		for (i = 0; i < ishtar_numberOfVariables; ++i)
		{
			if (ishtar_services[i].inSnapshot == 1)
			{
				// service id
				// length == 0 mean variable size, ONLY for char or UCHAR, with ended caracter equal to '\0'
				if (ishtar_services[i].length == 0 && ishtar_services[i].type == UCHAR)
				{
					length = ishtar_Strlen((char*)(ishtar_services[i].value));
					ishtar_SendData((unsigned char*)(&length), sizeof(ishtar_ValueVectorSize));

					for (j = 0; j < length; ++j)
					{
						ishtar_SendValue((unsigned char*)ishtar_services[i].value, ishtar_services[i].type, j);
					}

				}
				else
				{
					length = ishtar_services[i].length;
					for (j=0; j < length; ++j)
					{
						ishtar_SendValue((unsigned char*)ishtar_services[i].value, ishtar_services[i].type, j);
					}
				}
			}
		}

		ishtar_SendCk();

		ishtar_externalFlushFunc();
	}
}


/*!
*	This function reads the values from the memory and assign them to the Ishtar variables.
*
*	It first read the checksum in the 2 first bytes and checks if it is correct, then it reads and assigns the value.
*	The checksum is used to invalidate memory data when the variable list is changed.
*/
void ishtar_LoadFromMemory()
{
	unsigned short i;
	unsigned short j;
	unsigned short size;
	unsigned short nameSize;
	unsigned short addr = ishtar_memoryMinAddress;
	unsigned char readChecksumA, readChecksumB;

	if (!ishtar_memoryReadFunc)
	{
		ishtar_memoryError = READ_FUNC_NULL;
		return;
	}

	// read checksum first
	if (ishtar_memoryReadFunc(addr++, &readChecksumA, 1) != 1)
	{
		ishtar_memoryError = READ_ERROR;
		return;
	}
	ishtar_numberOfBytesReadFromMemory += 1;
	if (ishtar_memoryReadFunc(addr++, &readChecksumB, 1) != 1)
	{
		ishtar_memoryError = READ_ERROR;
		return;
	}
	ishtar_numberOfBytesReadFromMemory += 1;

	// verify checksums
	ishtar_memoryChecksumA = 0;
	ishtar_memoryChecksumB = 0;

	for (i = 0; i < ishtar_numberOfVariables; ++i)
	{
		if (ishtar_services[i].flags & PERSISTENT)
		{
			// the id, type, length and name are used in the checksum
			ishtar_memoryChecksumA += (unsigned char)ishtar_services[i].type;
			ishtar_memoryChecksumB += ishtar_memoryChecksumA;
			ishtar_memoryChecksumA += (unsigned char)ishtar_services[i].length;
			ishtar_memoryChecksumB += ishtar_memoryChecksumA;
			ishtar_memoryChecksumA += (unsigned char)i;
			ishtar_memoryChecksumB += ishtar_memoryChecksumA;

			nameSize = ishtar_Strlen(ishtar_services[i].name);
			for (j = 0; j < nameSize; ++j)
			{
				// checksum the name
				ishtar_memoryChecksumA += (unsigned char)ishtar_services[i].name[j];
				ishtar_memoryChecksumB += ishtar_memoryChecksumA;
			}
		}
	}
	
	// checksum is wrong
	if (!(readChecksumA == ishtar_memoryChecksumA && readChecksumB == ishtar_memoryChecksumB))
	{
		ishtar_memoryError = CHECKSUM_WRONG;
		return;
	}

	// assign the values
	for (i = 0; i < ishtar_numberOfVariables; ++i)
	{
		if (ishtar_services[i].flags & PERSISTENT)
		{
			// size of this variable in the memory
			size = (unsigned short)(ishtar_TypeSize(ishtar_services[i].type) * ishtar_services[i].length);
			// write value to memory
			if (size != ishtar_memoryReadFunc(addr, ishtar_services[i].value, size))
			{
				ishtar_memoryError = READ_ERROR;
				return;
			}	
			addr += size;
			ishtar_numberOfBytesReadFromMemory += size;
			
			// call feedback function if existing
			if (ishtar_services[i].feedbackFunc)
				ishtar_services[i].feedbackFunc(&ishtar_services[i]);
		}
	}

	ishtar_memoryError = 0;
}

/*!
*	This function writes the values of the Ishtar variables to the memory.
*
*	It first checks that the is enough space in the memory, then it writes the checksum in the first 2 bytes, and then the values.
*/
void ishtar_SaveToMemory()
{
	unsigned short i;
	unsigned short j;
	unsigned short size = 2;
	unsigned short nameSize;
	unsigned short addr = ishtar_memoryMinAddress + 2;

	if (!ishtar_memoryWriteFunc)
	{
		ishtar_memoryError = WRITE_FUNC_NULL;
		return;
	}

	// check size
	for (i = 0; i < ishtar_numberOfVariables; ++i)
	{
		if (ishtar_services[i].flags & PERSISTENT)
		{
			size += ishtar_TypeSize(ishtar_services[i].type) * ishtar_services[i].length;
		}
	}

	// checks if there is enough memory available
	if (ishtar_memoryMaxAddress - ishtar_memoryMinAddress < size)
	{
		ishtar_memoryError = NOT_ENOUGH_SPACE;
		return;
	}


	// write and calculate checksum
	ishtar_memoryChecksumA = 0;
	ishtar_memoryChecksumB = 0;

	for (i = 0; i < ishtar_numberOfVariables; ++i)
	{
		if (ishtar_services[i].flags & PERSISTENT)
		{
			// the id, type, length and name are used in the checksum
			ishtar_memoryChecksumA += (unsigned char)ishtar_services[i].type;
			ishtar_memoryChecksumB += ishtar_memoryChecksumA;
			ishtar_memoryChecksumA += (unsigned char)ishtar_services[i].length;
			ishtar_memoryChecksumB += ishtar_memoryChecksumA;
			ishtar_memoryChecksumA += (unsigned char)i;
			ishtar_memoryChecksumB += ishtar_memoryChecksumA;

			nameSize = ishtar_Strlen(ishtar_services[i].name);
			for (j = 0; j < nameSize; ++j)
			{
				// checksum the name
				ishtar_memoryChecksumA += (unsigned char)ishtar_services[i].name[j];
				ishtar_memoryChecksumB += ishtar_memoryChecksumA;
			}

			// size of this variable in the memory
			size = (unsigned short)(ishtar_TypeSize(ishtar_services[i].type) * ishtar_services[i].length);
			// write value to memory
			if (size != ishtar_memoryWriteFunc(addr, ishtar_services[i].value, size))
			{
				ishtar_memoryError = WRITE_ERROR;
				return;	
			}	
			addr += size;
			ishtar_numberOfBytesWrittenToMemory += size;
		}
	}

	// end writing checksum
	if (ishtar_memoryWriteFunc(ishtar_memoryMinAddress, &ishtar_memoryChecksumA, 1) != 1)
	{
		ishtar_memoryError = WRITE_ERROR;
		return;		
	}
	ishtar_numberOfBytesWrittenToMemory += 1;
	if (ishtar_memoryWriteFunc(ishtar_memoryMinAddress + 1, &ishtar_memoryChecksumB, 1) != 1)
	{
		ishtar_memoryError = WRITE_ERROR;
		return;		
	}
	ishtar_numberOfBytesWrittenToMemory += 1;

	ishtar_memoryError = NO_ERROR;
}

// init


/*!
*	This function inits Ishtar Embedded server
*
*	@param externalSendFunc The external function to send data
*	@param externalFlushFunc The external function to flush data output buffer
*
**/
void ishtar_Init(ishtar_ExternalSendFunc externalSendFunc, ishtar_ExternalFlushFunc externalFlushFunc)
{
	ishtar_externalSendFunc = externalSendFunc;
	ishtar_externalSendBlockingFunc = externalSendFunc;
	ishtar_currentSendFunc = externalSendFunc;

	if (externalFlushFunc)
	{
		ishtar_externalFlushFunc = externalFlushFunc;
	}
	else
	{
		ishtar_externalFlushFunc = &ishtar_DoNothing;
	}

	ishtar_InitBuffer();

	// DEBUG
#if ISHTAR_DEBUG
	ishtar_RegisterDebugVars();
#endif

}

/*!
*	This function inits Ishtar Embedded server with the use of
*	a different sending function for big messages.
*	The HELLO and SERVICE_LIST packets use the blocking function, other packets the non-blocking
*
*	WARNING: Using this function may cause the host to stuck for a while when the client asks for
*	the list of all services, as the blocking function could take *several seconds* to send the data
*
*	@param externalSendFunc The external function to send data
*	@param externalSendBlockFunc The external blocking function to send data (big packets)
*	@param externalFlushFunc The external function to flush data output buffer
*
**/
void ishtar_InitWithBlock(ishtar_ExternalSendFunc externalSendFunc, ishtar_ExternalSendFunc externalSendBlockFunc, ishtar_ExternalFlushFunc externalFlushFunc)
{
	ishtar_externalSendFunc = externalSendFunc;
	ishtar_externalSendBlockingFunc = externalSendBlockFunc;
	ishtar_currentSendFunc = externalSendFunc;

	if (externalFlushFunc)
	{
		ishtar_externalFlushFunc = externalFlushFunc;
	}
	else
	{
		ishtar_externalFlushFunc = &ishtar_DoNothing;
	}

	ishtar_InitBuffer();

	// DEBUG
#if ISHTAR_DEBUG
	ishtar_RegisterDebugVars();
#endif
}

/*!
*	This function inits the receive buffers and pointers.
*/
void ishtar_InitBuffer()
{
	ishtar_bufBegin = ishtar_buffer;
	ishtar_bufEnd = ishtar_buffer + ISHTAR_BUFFER_SIZE;
	ishtar_bufPos = ishtar_buffer;

	ishtar_consumePos = ishtar_buffer;

	ishtar_packetBegin = ishtar_buffer;
}

/*!
*	This function directly registers several variables to monitor Ishtar itself
*/
void ishtar_RegisterDebugVars()
{
	ishtar_Variable("ishtar.overflows", (unsigned char*)(&ishtar_numberOfOverflows), UINT, 1, 0);
	ishtar_Variable("ishtar.variables", (unsigned char*)(&ishtar_numberOfVariables), USHORT, 1, 0);
	ishtar_Variable("ishtar.dropHeader1", (unsigned char*)(&ishtar_numberOfDropHeader1), UINT, 1, 0);
	ishtar_Variable("ishtar.dropHeader2", (unsigned char*)(&ishtar_numberOfDropHeader2), UINT, 1, 0);
	ishtar_Variable("ishtar.dropHeader3", (unsigned char*)(&ishtar_numberOfDropHeader3), UINT, 1, 0);
	ishtar_Variable("ishtar.dropHeader4", (unsigned char*)(&ishtar_numberOfDropHeader4), UINT, 1, 0);
	ishtar_Variable("ishtar.dropSize", (unsigned char*)(&ishtar_numberOfDropSize), UINT, 1, 0);
	ishtar_Variable("ishtar.dropContent", (unsigned char*)(&ishtar_numberOfDropContent), UINT, 1, 0);
	ishtar_Variable("ishtar.bytesSent", (unsigned char*)(&ishtar_numberOfBytesSent), UINT, 1, 0);
	ishtar_Variable("ishtar.bytesReceived", (unsigned char*)(&ishtar_numberOfBytesReceived), UINT, 1, 0);
	ishtar_Variable("ishtar.validMessages", (unsigned char*)(&ishtar_numberOfValidMessages), UINT, 1, 0);
	ishtar_Variable("ishtar.memoryBytesRead", (unsigned char*)(&ishtar_numberOfBytesReadFromMemory), UINT, 1, 0);
	ishtar_Variable("ishtar.memoryBytesWritten", (unsigned char*)(&ishtar_numberOfBytesWrittenToMemory), UINT, 1, 0);
	ishtar_Variable("ishtar.memoryError", (unsigned char*)(&ishtar_memoryError), UCHAR, 1, 0);
	ishtar_Variable("ishtar.snapshotSize", (unsigned char*)(&ishtar_snapshotSize), UINT, 1, 0);
}

/*!
*	This function inits the non-volatile memory access functions
*
*	@param memoryReadFunc A pointer to the external function that reads the non-volatile memory
*	@param memoryWriteFunc A pointer to the external function that writes the non-volatile memory
*	@param minAddress The lowest memory address that Ishtar is allowed to write
*	@param maxAddress The highest memory address that Ishtar is allowed to write
*/
void ishtar_InitMemoryAccess(ishtar_MemoryReadFunc memoryReadFunc, ishtar_MemoryWriteFunc memoryWriteFunc, unsigned short minAddress, unsigned short maxAddress)
{
	ishtar_memoryReadFunc = memoryReadFunc;
	ishtar_memoryWriteFunc = memoryWriteFunc;

	ishtar_memoryMinAddress = minAddress;
	ishtar_memoryMaxAddress = maxAddress;
}

/*!
*	This function registers a variable into Ishtar in order to make it accessible to external clients
*
*	@param name The textual name of the variable. Use '.' character to create sub-groups, e.g. "gps.position.longitude"
*	@param var A pointer to the real variable or to the root of the array of variables
*	@param type The type of the variable : BOOL, CHAR, UCHAR, SHORT, USHORT, INT, UINT, FLOAT, DOUBLE
*	@param length The number of elements in the array, or 1 for a normal variable
*	@param flags The flags for this variable : 0 or READ_ONLY
*/
ishtar_ServiceId ishtar_Variable(const char* name, void* var, ishtar_ValueVectorType type, ishtar_ValueVectorSize length, ishtar_ServiceFlags flags)
{
	return ishtar_VariableFeedback(name, var, type, length, flags, 0);
}

/*!
*	This function registers a variable into Ishtar in order to make it accessible to external clients
*
*	@param name The textual name of the variable. Use '.' character to create sub-groups, e.g. "gps.position.longitude"
*	@param var A pointer to the real variable or to the root of the array of variables
*	@param type The type of the variable : BOOL, CHAR, UCHAR, SHORT, USHORT, INT, UINT, FLOAT, DOUBLE
*	@param length The number of elements in the array, or 1 for a normal variable
*	@param flags The flags for this variable : 0 or READ_ONLY
*	@param feedbackFunc Pointer to a function called whenever the corresponding variable is modified.
*/
ishtar_ServiceId ishtar_VariableFeedback(const char* name, void* var, ishtar_ValueVectorType type, ishtar_ValueVectorSize length, ishtar_ServiceFlags flags, ishtar_VariableModifiedFunc feedbackFunc)
{
	if (ishtar_numberOfVariables < ISHTAR_MAX_NUMBER_OF_SERVICES)
	{
		// If the length == 0, then the variable must be read-only because there is no dynamic allocation
		if (length == 0)
		{
			flags |= READ_ONLY;
		}
		ishtar_services[ishtar_numberOfVariables].name = name;
		ishtar_services[ishtar_numberOfVariables].type = type;
		ishtar_services[ishtar_numberOfVariables].length = length;
		ishtar_services[ishtar_numberOfVariables].flags = flags;
		ishtar_services[ishtar_numberOfVariables].value = var;
		ishtar_services[ishtar_numberOfVariables].inSnapshot = 0;
		ishtar_services[ishtar_numberOfVariables].feedbackFunc = feedbackFunc;
		

		return ishtar_numberOfVariables++;
	}
	return 0;
}


/*!
*	This function calculates the length of a zero-terminated string
*
*	@param The zero-terminated string for which the length will be calculated
*	@return The length of the string or ISHTAR_STRING_MAX_SIZE if the string is too long (to prevent from getting stuck)
*/
unsigned long ishtar_Strlen(const char * addr)
{
	unsigned long l = 0;
	while (*addr != '\0' && l < ISHTAR_STRING_MAX_SIZE)
	{
		++l;
		++addr;
	}
	return l;
}

/*!
*	This function returns the number of bytes taken by a variable of a certain type
*
*	@param type The IShtar-defined type of the variable : BOOL, CHAR, UCHAR, SHORT, USHORT, INT, UINT, FLOAT, DOUBLE
*/
unsigned long ishtar_TypeSize(ishtar_ValueVectorType type)
{
	unsigned long size = 0;
	if (type == CHAR || type == UCHAR || type == BOOL)
	{
		size = sizeof(char);
	}
	else if (type == SHORT || type == USHORT)
	{
		size = sizeof(short);
	}
	else if (type == FLOAT || type == INT || type == UINT)
	{
		size = sizeof(long);
	}
	else if (type == DOUBLE)
	{
		size = sizeof(double);
	}
	return size;
}

/*!
*	This function returns true if the server already received a HELLO message since it started, false otherwise
*/
unsigned char ishtar_IsConnected()
{
	return ishtar_connected;
}

unsigned short ishtar_ServiceCount()
{
	return ishtar_numberOfVariables;
}


#ifdef __cplusplus
}
#endif