/**
 * send_mbx send a message to a youbot motor slave.
 *
 * @param gripper 1 to address the gripper, 0 otherwise
 * @param instr_nr command number (GAP, SAP, ...)
 * @param param_nr type number
 * @param slave_nr slave index
 * @param bank_nr mostly zero. Motor or Bank number
 * @param value value to send and location to store result.
 *
 * @return 0 if Ok, -1 otherwise.
 */
int send_mbx(int gripper,
	     uint8_t instr_nr,
	     uint8_t param_nr,
	     uint16_t slave_nr,
	     uint8_t bank_nr,
	     int32_t *value)
{
	int ret=-1;
	char* msg;
	ec_mbxbuft mbx_out, mbx_in;

	mbx_out[0] = (gripper) ? 1 : 0;
	mbx_out[1] = instr_nr;
	mbx_out[2] = param_nr;
	mbx_out[3] = bank_nr;

	/* TODO: replace with htobe32 */
	mbx_out[4] = (uint32)(*value >> 24);
	mbx_out[5] = (uint32)(*value >> 16);
	mbx_out[6] = (uint32)(*value >> 8);
	mbx_out[7] = (uint32)(*value & 0xff);

	/* DBG("sending send mbx (gripper:%d, instr_nr=%d, param_nr=%d, slave_nr=%d, bank_nr=%d, value=%d", */
	/*     gripper, instr_nr, param_nr, slave_nr, bank_nr, *value); */

	if (ec_mbxsend(slave_nr, &mbx_out, EC_TIMEOUTTXM) <= 0) {
		ERR("failed to send mbx (gripper:%d, instr_nr=%d, param_nr=%d, slave_nr=%d, bank_nr=%d, value=%d",
		    gripper, instr_nr, param_nr, slave_nr, bank_nr, *value);
		goto out;
	}

	if (ec_mbxreceive(slave_nr, &mbx_in, EC_TIMEOUTRXM) <= 0) {
		ERR("failed to receive mbx (gripper:%d, instr_nr=%d, param_nr=%d, slave_nr=%d, bank_nr=%d, value=%d",
		    gripper, instr_nr, param_nr, slave_nr, bank_nr, *value);
		goto out;
	}

	*value = (mbx_in[4] << 24 | mbx_in[5] << 16 | mbx_in[6] << 8 | mbx_in[7]);

	switch(((int) mbx_in[2])) {
	case YB_MBX_STAT_OK: goto out_ok;
	case YB_MBX_STAT_INVALID_CMD: msg="invalid command"; break;
	case YB_MBX_STAT_WRONG_TYPE: msg="wrong type"; break;
	case YB_MBX_STAT_EINVAL: msg="invalid value"; break;
	case YB_MBX_STAT_EEPROM_LOCKED: msg="Configuration EEPROM locked"; break;
	case YB_MBX_STAT_CMD_UNAVAILABLE: msg="Command not available"; break;
	case YB_MBX_STAT_PARAM_PWDPROT: msg="Parameter is password protected"; break;
	default: msg="unkown error";
	}

	if(((int) mbx_in[2]) != 100) {
		ERR("receiving mbx failed: module=%d, status=%d, command=%d, value=%d: %s.",
		    mbx_in[1], mbx_in[2], mbx_in[3], *value, msg);
		goto out;
	}
out_ok:
	ret=0;
out:
	return ret;
}
///sends the mailbox Messages which have been stored in the buffer
///@param mailboxMsg ethercat mailbox message
bool EthercatMaster::sendMailboxMessage(const YouBotSlaveMailboxMsg& mailboxMsg) {
  // Bouml preserved body begin 00052F71
    //  LOG(trace) << "send mailbox message (buffer two) slave " << mailboxMsg.getSlaveNo();
    mailboxBufferSend[0] = mailboxMsg.stctOutput.moduleAddress;
    mailboxBufferSend[1] = mailboxMsg.stctOutput.commandNumber;
    mailboxBufferSend[2] = mailboxMsg.stctOutput.typeNumber;
    mailboxBufferSend[3] = mailboxMsg.stctOutput.motorNumber;
    mailboxBufferSend[4] = mailboxMsg.stctOutput.value >> 24;
    mailboxBufferSend[5] = mailboxMsg.stctOutput.value >> 16;
    mailboxBufferSend[6] = mailboxMsg.stctOutput.value >> 8;
    mailboxBufferSend[7] = mailboxMsg.stctOutput.value & 0xff;
    if (ec_mbxsend(mailboxMsg.getSlaveNo(), &mailboxBufferSend, mailboxTimeout)) {
      return true;
    } else {
      return false;
    }
  // Bouml preserved body end 00052F71
}
/** SoE write, blocking.
 * 
 * The IDN object of the selected slave and DriveNo is written. If a response
 * is larger than the mailbox size then the response is segmented.
 *
 * @param[in]  slave		= Slave number
 * @param[in]  driveNo		= Drive number in slave
 * @param[in]  elementflags = Flags to select what properties of IDN are to be transfered.
 * @param[in]  idn			= IDN.
 * @param[in]  psize		= Size in bytes of parameter buffer.
 * @param[out] p			= Pointer to parameter buffer
 * @param[in]  timeout		= Timeout in us, standard is EC_TIMEOUTRXM
 * @return Workcounter from last slave response
 */
int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout)
{
    ec_SoEt *SoEp, *aSoEp;
    uint16 framedatasize, maxdata;
	int wkc;
    uint8 *mp;
	uint8 *hp;
	uint16 *errorcode;
    ec_mbxbuft MbxIn, MbxOut;
    uint8 cnt;
	boolean NotLast;

    ec_clearmbx(&MbxIn);
	/* Empty slave out mailbox if something is in. Timeout set to 0 */
    wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, 0);
    ec_clearmbx(&MbxOut);
    aSoEp = (ec_SoEt *)&MbxIn;
    SoEp = (ec_SoEt *)&MbxOut;
    SoEp->MbxHeader.address = htoes(0x0000);
    SoEp->MbxHeader.priority = 0x00;
	SoEp->opCode = ECT_SOE_WRITEREQ;
	SoEp->error = 0;
	SoEp->driveNo = driveNo;
	SoEp->elementflags = elementflags;
	hp = p;
	mp = (uint8 *)&MbxOut + sizeof(ec_SoEt);
    maxdata = ec_slave[slave].mbx_l - sizeof(ec_SoEt);
	NotLast = TRUE;
	while (NotLast)
	{	
		framedatasize = psize;
		NotLast = FALSE;
		SoEp->idn = htoes(idn);
		SoEp->incomplete = 0;
		if (framedatasize > maxdata)
		{
			framedatasize = maxdata;  /*  segmented transfer needed  */
			NotLast = TRUE;
			SoEp->incomplete = 1;
			SoEp->fragmentsleft = psize / maxdata;
		}
		SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert) + framedatasize);
		/* get new mailbox counter, used for session handle */
		cnt = ec_nextmbxcnt(ec_slave[slave].mbx_cnt);
		ec_slave[slave].mbx_cnt = cnt;
		SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */
		/* copy parameter data to mailbox */
		memcpy(mp, hp, framedatasize);
		hp += framedatasize;
		psize -= framedatasize;
		/* send SoE request to slave */
		wkc = ec_mbxsend(slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
		if (wkc > 0) /* succeeded to place mailbox in slave ? */
		{
			if (!NotLast || !ec_mbxempty(slave, timeout))
			{	
				/* clean mailboxbuffer */
				ec_clearmbx(&MbxIn);
				/* read slave response */
				wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, timeout);
				if (wkc > 0) /* succeeded to read slave response ? */
				{
					NotLast = FALSE;
					/* slave response should be SoE, WriteRes */
					if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
					    (aSoEp->opCode == ECT_SOE_WRITERES) &&
					    (aSoEp->error == 0) &&
					    (aSoEp->driveNo == driveNo) &&
					    (aSoEp->elementflags == elementflags))
					{
						/* SoE write succeeded */
					}	
					/* other slave response */
					else
					{
						if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
						    (aSoEp->opCode == ECT_SOE_READRES) &&
						    (aSoEp->error == 1))
						{
							mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
							errorcode = (uint16 *)mp;
							ec_SoEerror(slave, idn, *errorcode);
						}
						else
						{
							ec_packeterror(slave, idn, 0, 1); /* Unexpected frame returned */
						}
						wkc = 0;
					}
				}
				else
				{
					ec_packeterror(slave, idn, 0, 4); /* no response */
				}	
			}	
		}	
	}
    return wkc;
}
/** SoE read, blocking.
 * 
 * The IDN object of the selected slave and DriveNo is read. If a response
 * is larger than the mailbox size then the response is segmented. The function
 * will combine all segments and copy them to the parameter buffer.
 *
 * @param[in]  slave		= Slave number
 * @param[in]  driveNo		= Drive number in slave
 * @param[in]  elementflags = Flags to select what properties of IDN are to be transfered.
 * @param[in]  idn			= IDN.
 * @param[in,out] psize		= Size in bytes of parameter buffer, returns bytes read from SoE.
 * @param[out] p			= Pointer to parameter buffer
 * @param[in]  timeout		= Timeout in us, standard is EC_TIMEOUTRXM
 * @return Workcounter from last slave response
 */
int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout)
{
    ec_SoEt *SoEp, *aSoEp;
    uint16 totalsize, framedatasize;
	int wkc;
    uint8 *bp;
	uint8 *mp;
	uint16 *errorcode;
    ec_mbxbuft MbxIn, MbxOut;
    uint8 cnt;
	boolean NotLast;

    ec_clearmbx(&MbxIn);
	/* Empty slave out mailbox if something is in. Timeout set to 0 */
    wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, 0);
    ec_clearmbx(&MbxOut);
    aSoEp = (ec_SoEt *)&MbxIn;
    SoEp = (ec_SoEt *)&MbxOut;
    SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert));
    SoEp->MbxHeader.address = htoes(0x0000);
    SoEp->MbxHeader.priority = 0x00;
	/* get new mailbox count value, used as session handle */
    cnt = ec_nextmbxcnt(ec_slave[slave].mbx_cnt);
    ec_slave[slave].mbx_cnt = cnt;
    SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */
	SoEp->opCode = ECT_SOE_READREQ;
	SoEp->incomplete = 0;
	SoEp->error = 0;
	SoEp->driveNo = driveNo;
	SoEp->elementflags = elementflags;
	SoEp->idn = htoes(idn);
	totalsize = 0;
	bp = p;
	mp = (uint8 *)&MbxIn + sizeof(ec_SoEt);
	NotLast = TRUE;
	/* send SoE request to slave */
    wkc = ec_mbxsend(slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
    if (wkc > 0) /* succeeded to place mailbox in slave ? */
	{
		while (NotLast)
		{	
			/* clean mailboxbuffer */
			ec_clearmbx(&MbxIn);
			/* read slave response */
			wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, timeout);
			if (wkc > 0) /* succeeded to read slave response ? */
			{
				/* slave response should be SoE, ReadRes */
				if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
				    (aSoEp->opCode == ECT_SOE_READRES) &&
				    (aSoEp->error == 0) &&
				    (aSoEp->driveNo == driveNo) &&
				    (aSoEp->elementflags == elementflags))
				{
					framedatasize = etohs(aSoEp->MbxHeader.length) - sizeof(ec_SoEt)  + sizeof(ec_mbxheadert);
					totalsize += framedatasize;
					/* Does parameter fit in parameter buffer ? */
					if (totalsize <= *psize)
					{
						/* copy parameter data in parameter buffer */
						memcpy(bp, mp, framedatasize);
						/* increment buffer pointer */
						bp += framedatasize;
					}
					else
					{
						framedatasize -= totalsize - *psize;
						totalsize = *psize;
						/* copy parameter data in parameter buffer */
						if (framedatasize > 0) memcpy(bp, mp, framedatasize);
					}	

					if (!aSoEp->incomplete)	
					{
						NotLast = FALSE;
						*psize = totalsize;
					}	
				}	
				/* other slave response */
				else
				{
					NotLast = FALSE;
					if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
					    (aSoEp->opCode == ECT_SOE_READRES) &&
					    (aSoEp->error == 1))
					{
						mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
						errorcode = (uint16 *)mp;
						ec_SoEerror(slave, idn, *errorcode);
					}
					else
					{
						ec_packeterror(slave, idn, 0, 1); /* Unexpected frame returned */
					}
					wkc = 0;
				}
			}
			else
			{
				NotLast = FALSE;
				ec_packeterror(slave, idn, 0, 4); /* no response */
			}	
		}	
	}
    return wkc;
}
/** FoE read, blocking.
 * 
 * @param[in]  slave		= Slave number.
 * @param[in]  filename		= Filename of file to read.
 * @param[in]  password     = password.
 * @param[in,out] psize		= Size in bytes of file buffer, returns bytes read from file.
 * @param[out] p			= Pointer to file buffer
 * @param[in]  timeout		= Timeout in us, standard is EC_TIMEOUTRXM
 * @return Workcounter from last slave response
 */
int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout)
{
    ec_FOEt *FOEp, *aFOEp;
	int wkc;
    int32 dataread = 0;
	int32 buffersize, packetnumber, prevpacket = 0;
	uint16 fnsize, maxdata, segmentdata;
    ec_mbxbuft MbxIn, MbxOut;
    uint8 cnt;
    boolean worktodo;

	buffersize = *psize;
    ec_clearmbx(&MbxIn);
	/* Empty slave out mailbox if something is in. Timout set to 0 */
    wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, 0);
    ec_clearmbx(&MbxOut);
    aFOEp = (ec_FOEt *)&MbxIn;
    FOEp = (ec_FOEt *)&MbxOut;
	fnsize = strlen(filename);
	maxdata = ec_slave[slave].mbx_l - 12;
	if (fnsize > maxdata)
		fnsize = maxdata;
    FOEp->MbxHeader.length = htoes(0x0006 + fnsize);
    FOEp->MbxHeader.address = htoes(0x0000);
    FOEp->MbxHeader.priority = 0x00;
	/* get new mailbox count value, used as session handle */
    cnt = ec_nextmbxcnt(ec_slave[slave].mbx_cnt);
    ec_slave[slave].mbx_cnt = cnt;
    FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
    FOEp->OpCode = ECT_FOE_READ;
    FOEp->Password = htoel(password);
	/* copy filename in mailbox */
	memcpy(&FOEp->FileName[0], filename, fnsize);
	/* send FoE request to slave */
    wkc = ec_mbxsend(slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
    if (wkc > 0) /* succeeded to place mailbox in slave ? */
    {
		do
		{	
			worktodo = FALSE;
			/* clean mailboxbuffer */
			ec_clearmbx(&MbxIn);
			/* read slave response */
			wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, timeout);
			if (wkc > 0) /* succeeded to read slave response ? */
			{
				/* slave response should be FoE */
				if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE)
				{
					if(aFOEp->OpCode == ECT_FOE_DATA)
					{
						segmentdata = etohs(aFOEp->MbxHeader.length) - 0x0006;
						packetnumber = etohl(aFOEp->PacketNumber);
						if ((packetnumber == ++prevpacket) && (dataread + segmentdata <= buffersize))
						{
							memcpy(p, &aFOEp->Data[0], segmentdata);
							dataread += segmentdata;
							p += segmentdata;
							if (segmentdata == maxdata)
								worktodo = TRUE; 
							FOEp->MbxHeader.length = htoes(0x0006);
							FOEp->MbxHeader.address = htoes(0x0000);
							FOEp->MbxHeader.priority = 0x00;
							/* get new mailbox count value */
							cnt = ec_nextmbxcnt(ec_slave[slave].mbx_cnt);
							ec_slave[slave].mbx_cnt = cnt;
							FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
							FOEp->OpCode = ECT_FOE_ACK;
							FOEp->PacketNumber = htoel(packetnumber);
							/* send FoE ack to slave */
							wkc = ec_mbxsend(slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
							if (wkc <= 0)
								worktodo = FALSE;
						}
						else
						{
							/* FoE error */
							wkc = -EC_ERR_TYPE_FOE_BUF2SMALL;
						}
					}
					else
					if(aFOEp->OpCode == ECT_FOE_ERROR)
					{
						/* FoE error */
						wkc = -EC_ERR_TYPE_FOE_ERROR;
					}
					else
					{
						/* unexpected mailbox received */
						wkc = -EC_ERR_TYPE_PACKET_ERROR;
					}
				}
				else
				{
					/* unexpected mailbox received */
					wkc = -EC_ERR_TYPE_PACKET_ERROR;
				}
				*psize = dataread;
			}
		} while (worktodo);	
	}
	
	return wkc;
}