
//OpenSCADA module DAQ.ICP_DAS file: da_ISA.cpp
/***************************************************************************
 *   Copyright (C) 2012-2025 by Roman Savochenko, <roman@oscada.org>       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; version 2 of the License.               *
 *                                                                         *
 *   This program 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 General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#include "ixisa.h"

#include <tsys.h>

#include "da_ISA.h"
#include "ICP_module.h"

using namespace ICP_DAS_DAQ;

//*************************************************
//* CPU                                           *
//*************************************************
da_ISA::da_ISA( )
{
    devs["A-626"]	= DevFeature(0, 6, 0x0002, 0x0002);
    devs["A-628"]	= DevFeature(0, 8, 0x0202, 0x0202);
    devs["A-8111"]	= DevFeature(0x1108, 2, 0x0102, 0x0102);
    devs["A-812"]	= DevFeature(0x0110, 2, 0x0102, 0x0102);
    devs["A-821"]	= DevFeature(0x1010, 1, 0x0102, 0x0102);
    devs["A-822"]	= DevFeature(0x0110, 2, 0x0102, 0x0102);
    devs["A-823"]	= DevFeature(0x0110, 2, 0x0102, 0x0102);
    devs["A-826"]	= DevFeature(0x0110, 2, 0x0102, 0x0102);
    devs["DIO-144"]	= DevFeature(0, 0, 6);
    devs["DIO-24"]	= DevFeature(0, 0, 1);
    devs["DIO-48"]	= DevFeature(0, 0, 3);			//Real present two channels from 1 to 2
    devs["DIO-64"]	= DevFeature(0, 0, 0x0204, 0x0204);
    devs["DIO-96"]	= DevFeature(0, 0, 4);
    devs["ISO-730"]	= DevFeature(0, 0, 0x0204, 0x0204);	//Specific for isolation separated (first 2 channels)
    devs["ISO-813"]	= DevFeature(32);
    devs["ISO-C64"]	= DevFeature(0, 0, 0, 0, 0x0008);
    devs["ISO-P32C32"]	= DevFeature(0, 0, 0x0204, 0x0204);
    devs["ISO-P64"]	= DevFeature(0, 0, 0, 0x0008);
    devs["P16R16DIO"]	= DevFeature(0, 0, 0x0002, 0x0002);
    devs["P8R8DIO"]	= DevFeature(0, 0, 0x0101, 0x0101);
    devs["TMC-10"]	= DevFeature(0, 0, 0x0002, 0x0001);
}

da_ISA::~da_ISA( )
{

}

string da_ISA::modType( TMdPrm *prm, const string &modTp )
{
    FILE *fd_proc = fopen(IXISA_PROC_FILE, "r");
    if(fd_proc) {
	char rbuf[200], isadev[31], isaname[31];
	while(fgets(rbuf,sizeof(rbuf),fd_proc))
	    if(sscanf(rbuf,"dev: %30s %*x %*x %30s",isadev,isaname) == 2 && modTp == isadev) {
		if(fclose(fd_proc) != 0)
		    mess_warning(prm->nodePath().c_str(), _("Closing the file %p error '%s (%d)'!"), fd_proc, strerror(errno), errno);
		return isaname;
	    }
	if(fclose(fd_proc) != 0)
	    mess_warning(prm->nodePath().c_str(), _("Closing the file %p error '%s (%d)'!"), fd_proc, strerror(errno), errno);
    }

    return "";
}

void da_ISA::tpList( TMdPrm *prm, vector<string> &tpl, vector<string> *ntpl )
{
    //Only for ISA boards
    if(prm->owner().bus() != -1) return;
    FILE *fd_proc = fopen(IXISA_PROC_FILE, "r");
    if(fd_proc) {
	char rbuf[200], isadev[31], isaname[31];
	while(fgets(rbuf,sizeof(rbuf),fd_proc))
	    if(sscanf(rbuf,"dev: %30s %*x %*x %30s",isadev,isaname) == 2)
	    { tpl.push_back(isadev); if(ntpl) ntpl->push_back(TSYS::strMess("%s (%s)",isaname,isadev)); }
	if(fclose(fd_proc) != 0)
	    mess_warning(prm->nodePath().c_str(), _("Closing the file %p error '%s (%d)'!"), fd_proc, strerror(errno), errno);
    }
}

void da_ISA::enable( TMdPrm *p, vector<string> &als )
{
    string chnId, chnNm;

    if(!p->extPrms) p->extPrms = new tval();
    tval *ePrm = (tval*)p->extPrms;

    ePrm->dev = devs[modType(p,p->modTp.getS())];
    ePrm->devFd = open(("/dev/"+p->modTp.getS()).c_str(), O_RDWR);
    if(ePrm->devFd < 0)	return;

    MtxAlloc res(p->owner().pBusRes, true);

    //AI processing
    if(ePrm->dev.AI) {
	ePrm->dev.aiTm = s2i(p->modPrm("aiTm","200"));
	for(unsigned i_a = 0; i_a < (ePrm->dev.AI&0xFF); i_a++) {
	    chnId = TSYS::strMess("ai%d",i_a); chnNm = TSYS::strMess(_("Analog input %d"),i_a);
	    p->pEl.fldAt(p->pEl.fldAdd(new TFld(chnId.c_str(),chnNm.c_str(),TFld::Integer,TFld::NoWrite))).
		setReserve(p->modPrm(TSYS::strMess("aiGain.%d",i_a)));
	    als.push_back(chnId);
	}
	// Switch to software trigger/transfer mode
	ixisa_reg_t reg;
	reg.id = IXISA_ADMCR;
	if((reg.value=(ePrm->dev.AI>>8))) ioctl(ePrm->devFd, IXISA_WRITE_REG, &reg);
    }

    //AO processing
    for(unsigned i_a = 0; i_a < ePrm->dev.AO; i_a++) {
	chnId = TSYS::strMess("ao%d",i_a); chnNm = TSYS::strMess(_("Analog output %d"),i_a);
	p->pEl.fldAt(p->pEl.fldAdd(new TFld(chnId.c_str(),chnNm.c_str(),TFld::Integer,TVal::DirWrite)));
	als.push_back(chnId);
    }

    //DIO processing
    if(ePrm->dev.DIO) {
	int directDIO = s2i(p->modPrm("DirectDIO"));
	for(unsigned iCh = 0; iCh < ePrm->dev.DIO; iCh++) {
	    p->dInOutRev[iCh] = s2i(p->modPrm("dIORev"+i2s(iCh)));
	    // Set board configuration
	    ixisa_reg_t reg;
	    reg.value = (directDIO&(1<<iCh)) ? 0x80 : 0x9b;
	    if((directDIO&(1<<iCh)) && modType(p,p->modTp.getS()) == "DIO-24") reg.value = 0x89;

	    if(ePrm->dev.DIO == 1) reg.id = IXISA_CR;
	    else switch(iCh) {
		case 0: reg.id = IXISA_CN0CR;   break;
		case 1: reg.id = IXISA_CN1CR;   break;
		case 2: reg.id = IXISA_CN2CR;   break;
		case 3: reg.id = IXISA_CN3CR;   break;
		case 4: reg.id = IXISA_CN4CR;   break;
		case 5: reg.id = IXISA_CN5CR;   break;
	    }
	    ioctl(ePrm->devFd, IXISA_WRITE_REG, &reg);

	    //Attributes create
	    if((directDIO>>iCh)&1)
		for(int iO = 0; iO < 24; iO++) {
		    chnId = TSYS::strMess("o%d_%d",iCh,iO); chnNm = TSYS::strMess(_("Digital out %d.%d"),iCh,iO);
		    p->pEl.fldAdd(new TFld(chnId.c_str(),chnNm.c_str(),TFld::Boolean,TVal::DirWrite));
		    als.push_back(chnId);
		}
	    else
		for(int iI = 0; iI < 24; iI++) {
		    chnId = TSYS::strMess("i%d_%d",iCh,iI); chnNm = TSYS::strMess(_("Digital input %d.%d"),iCh,iI);
		    p->pEl.fldAdd(new TFld(chnId.c_str(),chnNm.c_str(),TFld::Boolean,TFld::NoWrite));
		    als.push_back(chnId);
		}
	}
    }

    //DI and DO processing
    for(unsigned iCh = 0; iCh < ((ePrm->dev.DI&0xFF)+(ePrm->dev.DO&0xFF)); iCh++) {
	// Reverse configuration load
	p->dInOutRev[iCh] = s2i(p->modPrm("dIORev"+i2s(iCh)));

	// Attributes create
	if(iCh < (ePrm->dev.DI&0xFF))
	    for(int iI = 0; iI < 8; iI++) {
		chnId = TSYS::strMess("i%d_%d",iCh,iI); chnNm = TSYS::strMess(_("Digital input %d.%d"),iCh,iI);
		p->pEl.fldAdd(new TFld(chnId.c_str(),chnNm.c_str(),TFld::Boolean,TFld::NoWrite));
		als.push_back(chnId);
	    }
	else
	    for(int iO = 0; iO < 8; iO++) {
		chnId = TSYS::strMess("o%d_%d",iCh-(ePrm->dev.DI&0xFF),iO); chnNm = TSYS::strMess(_("Digital out %d.%d"),iCh-(ePrm->dev.DI&0xFF),iO);
		p->pEl.fldAdd(new TFld(chnId.c_str(),chnNm.c_str(),TFld::Boolean,TVal::DirWrite));
		als.push_back(chnId);
	    }
    }
}

void da_ISA::disable( TMdPrm *p )
{
    if(p->extPrms) {
	tval *ePrm = (tval*)p->extPrms;
	if(ePrm->devFd >= 0 && close(ePrm->devFd) != 0)
	    mess_warning(p->nodePath().c_str(), _("Closing the file %d error '%s (%d)'!"), ePrm->devFd, strerror(errno), errno);
	delete (tval *)p->extPrms;
	p->extPrms = NULL;
    }
}

void da_ISA::getVal( TMdPrm *p )
{
    tval *ePrm = (tval*)p->extPrms;
    if(ePrm->devFd < 0) return;

    MtxAlloc res(p->owner().pBusRes, true);

    //AI processing
    if(ePrm->dev.AI) {
	ixisa_reg_t reg, rai;
	rai.id = IXISA_AI;
	rai.mode = IXISA_RM_TRIGGER;

	int pGain = -1, curGain;
	for(unsigned i_a = 0; i_a < (ePrm->dev.AI&0xFF); i_a++) {
	    int rez = 0;
	    AutoHD<TVal> val = p->vlAt(TSYS::strMess("ai%d",i_a));
	    // Setup gain
	    curGain = s2i(val.at().fld().reserve());
	    if(curGain != pGain) {
		reg.id = IXISA_ADGCR;
		reg.value = pGain = curGain;
		rez = ioctl(ePrm->devFd, IXISA_WRITE_REG, &reg);
	    }
	    // Switch channel
	    reg.id = IXISA_ADMXCR;
	    reg.value = i_a;
	    if(!rez) rez = ioctl(ePrm->devFd, IXISA_WRITE_REG, &reg);
	    TSYS::sysSleep((float)ePrm->dev.aiTm*1e-6);
	    // Read value
	    if(!rez) rez = ioctl(ePrm->devFd, IXISA_READ_REG, &rai);

	    val.at().setI(rez?EVAL_REAL:(rai.value), 0, true);
	}
    }

    //DIO processing
    if(ePrm->dev.DIO) {
	int directDIO = s2i(p->modPrm("DirectDIO"));
	for(unsigned iCh = 0; iCh < ePrm->dev.DIO; iCh++)
	    for(int iP = 0; iP < 3; iP++) {
		ixisa_reg_t data;
		switch(iP) {
		    case 0:	//Port A
			if(ePrm->dev.DIO == 1) data.id = IXISA_PA;
			else switch(iCh) {
			    case 0: data.id = IXISA_CN0PA;	break;
			    case 1: data.id = IXISA_CN1PA;	break;
			    case 2: data.id = IXISA_CN2PA;	break;
			    case 3: data.id = IXISA_CN3PA;	break;
			    case 4: data.id = IXISA_CN4PA;	break;
			    case 5: data.id = IXISA_CN5PA;	break;
			}
			break;
		    case 1:	//Port B
			if(ePrm->dev.DIO == 1) data.id = IXISA_PB;
			else switch(iCh) {
			    case 0: data.id = IXISA_CN0PB;	break;
			    case 1: data.id = IXISA_CN1PB;	break;
			    case 2: data.id = IXISA_CN2PB;	break;
			    case 3: data.id = IXISA_CN3PB;	break;
			    case 4: data.id = IXISA_CN4PB;	break;
			    case 5: data.id = IXISA_CN5PB;	break;
			}
			break;
		    case 2:	//Port C
			if(ePrm->dev.DIO == 1) data.id = IXISA_PC;
			else switch(iCh) {
			    case 0: data.id = IXISA_CN0PC;	break;
			    case 1: data.id = IXISA_CN1PC;	break;
			    case 2: data.id = IXISA_CN2PC;	break;
			    case 3: data.id = IXISA_CN3PC;	break;
			    case 4: data.id = IXISA_CN4PC;	break;
			    case 5: data.id = IXISA_CN5PC;	break;
			}
			break;
		}
		int rez = ioctl(ePrm->devFd, IXISA_READ_REG, &data);
		if((directDIO>>iCh)&1)
		    for(int iO = 0; iO < 8; iO++)
			p->vlAt(TSYS::strMess("o%d_%d",iCh,iP*8+iO)).at().setB(rez?EVAL_BOOL:(((p->dInOutRev[iCh]>>(iP*8))^data.value)>>iO)&1, 0, true);
		else
		    for(int iI = 0; iI < 8; iI++)
			p->vlAt(TSYS::strMess("i%d_%d",iCh,iP*8+iI)).at().setB(rez?EVAL_BOOL:(((p->dInOutRev[iCh]>>(iP*8))^data.value)>>iI)&1, 0, true);
	    }
    }

    //DI processing
    for(int iCh = 0; iCh < (ePrm->dev.DI&0xFF); iCh++) {
	ixisa_reg_t data;
	switch(ePrm->dev.DI>>8) {
	    case 0:		//IXISA_DI_A(N)
		switch(iCh) {
		    case 0: data.id = IXISA_DI_A;	break;
		    case 1: data.id = IXISA_DI_B;	break;
		    case 2: data.id = IXISA_DI_C;	break;
		    case 3: data.id = IXISA_DI_D;	break;
		    case 4: data.id = IXISA_DI_E;	break;
		    case 5: data.id = IXISA_DI_F;	break;
		    case 6: data.id = IXISA_DI_G;	break;
		    case 7: data.id = IXISA_DI_H;	break;
		}
		break;
	    case 1:		//1-IXISA_DIL(H)
		if((ePrm->dev.DI&0xFF) == 1) data.id = IXISA_DI;
		else switch(iCh) {
		    case 0: data.id = IXISA_DIL;	break;
		    case 1: data.id = IXISA_DIH;	break;
		}
		break;
	    case 2:		//2-IXISA_DIO_A(N)
		if(modType(p,p->modTp.getS()) == "ISO-730")
		    switch(iCh) {
			case 0: data.id = IXISA_IDIO_A;	break;
			case 1: data.id = IXISA_IDIO_B;	break;
			case 2: data.id = IXISA_DIO_A;	break;
			case 3: data.id = IXISA_DIO_B;	break;
		    }
		else switch(iCh) {
		    case 0: data.id = IXISA_DIO_A;	break;
		    case 1: data.id = IXISA_DIO_B;	break;
		    case 2: data.id = IXISA_DIO_C;	break;
		    case 3: data.id = IXISA_DIO_D;	break;
		}
		break;
	}
	int rez = ioctl(ePrm->devFd, IXISA_READ_REG, &data);
	for(int iI = 0; iI < 8; iI++)
	    p->vlAt(TSYS::strMess("i%d_%d",iCh,iI)).at().setB(rez?EVAL_BOOL:((p->dInOutRev[iCh]^data.value)>>iI)&1, 0, true);
    }
}

void da_ISA::vlSet( TMdPrm *p, TVal &vo, const TVariant &vl, const TVariant &pvl )
{
    tval *ePrm = (tval*)p->extPrms;
    if(ePrm->devFd < 0) return;

    MtxAlloc res(p->owner().pBusRes, true);

    //AO processing
    if(vo.name().compare(0,2,"ao") == 0 && ePrm->dev.AO) {
	int iCh = atoi(vo.name().c_str()+2);
	ixisa_reg_t reg;

	if(ePrm->dev.AO == 1)	reg.id = IXISA_AO;
	else switch(iCh) {
	    case 0:	reg.id = IXISA_AO0;	break;
	    case 1:	reg.id = IXISA_AO1;	break;
	    case 2:	reg.id = IXISA_AO2;	break;
	    case 3:	reg.id = IXISA_AO3;	break;
	    case 4:	reg.id = IXISA_AO4;	break;
	    case 5:	reg.id = IXISA_AO5;	break;
	    case 6:	reg.id = IXISA_AO6;	break;
	    case 7:	reg.id = IXISA_AO7;	break;
	}
	reg.value = vl.getI();
	ioctl(ePrm->devFd, IXISA_WRITE_REG, &reg);
    }

    //DIO processing
    if(vo.name().compare(0,1,"o") == 0 && ePrm->dev.DIO) {
	int iCh = 0, iP = 0;
	if(sscanf(vo.name().c_str(),"o%d_%d",&iCh,&iP) != 2) return;
	iP = iP/8;
	ixisa_reg_t data;
	switch(iP) {
	    case 0:	//Port A
		if(ePrm->dev.DIO == 1)	data.id = IXISA_PA;
		else switch(iCh) {
		    case 0: data.id = IXISA_CN0PA;	break;
		    case 1: data.id = IXISA_CN1PA;	break;
		    case 2: data.id = IXISA_CN2PA;	break;
		    case 3: data.id = IXISA_CN3PA;	break;
		    case 4: data.id = IXISA_CN4PA;	break;
		    case 5: data.id = IXISA_CN5PA;	break;
		}
		break;
	    case 1:	//Port B
		if(ePrm->dev.DIO == 1)	data.id = IXISA_PB;
		else switch(iCh) {
		    case 0: data.id = IXISA_CN0PB;	break;
		    case 1: data.id = IXISA_CN1PB;	break;
		    case 2: data.id = IXISA_CN2PB;	break;
		    case 3: data.id = IXISA_CN3PB;	break;
		    case 4: data.id = IXISA_CN4PB;	break;
		    case 5: data.id = IXISA_CN5PB;	break;
		}
		break;
	    case 2:	//Port C
		if(ePrm->dev.DIO == 1)	data.id = IXISA_PC;
		else switch(iCh) {
		    case 0: data.id = IXISA_CN0PC;	break;
		    case 1: data.id = IXISA_CN1PC;	break;
		    case 2: data.id = IXISA_CN2PC;	break;
		    case 3: data.id = IXISA_CN3PC;	break;
		    case 4: data.id = IXISA_CN4PC;	break;
		    case 5: data.id = IXISA_CN5PC;	break;
		}
		break;
	}
	data.value = 0;
	for(int iO = 7; iO >= 0; iO--) {
	    data.value = data.value << 1;
	    if(p->vlAt(TSYS::strMess("o%d_%d",iCh,iP*8+iO)).at().getB(0, true)) data.value |= 1;
	}
	data.value ^= (p->dInOutRev[iCh]>>(iP*8))&0xFF;
	ioctl(ePrm->devFd, IXISA_WRITE_REG, &data);
    }

    //DO processing
    if(vo.name().compare(0,1,"o") == 0 && ePrm->dev.DO) {
	int iCh = 0, iP = 0;
	if(sscanf(vo.name().c_str(),"o%d_%d",&iCh,&iP) != 2) return;
	ixisa_reg_t data;

	switch(ePrm->dev.DI>>8) {
	    case 0:		//IXISA_DO_A(N)
		switch(iCh) {
		    case 0: data.id = IXISA_DO_A;	break;
		    case 1: data.id = IXISA_DO_B;	break;
		    case 2: data.id = IXISA_DO_C;	break;
		    case 3: data.id = IXISA_DO_D;	break;
		    case 4: data.id = IXISA_DO_E;	break;
		    case 5: data.id = IXISA_DO_F;	break;
		    case 6: data.id = IXISA_DO_G;	break;
		    case 7: data.id = IXISA_DO_H;	break;
		}
		break;
	    case 1:		//1-IXISA_DOL(H)
		if((ePrm->dev.DI&0xFF) == 1)	data.id = IXISA_DO;
		else switch(iCh) {
		    case 0: data.id = IXISA_DOL;	break;
		    case 1: data.id = IXISA_DOH;	break;
		}
		break;
		case 2:		//2-IXISA_DIO_A(N)
		if(modType(p,p->modTp.getS()) == "ISO-730")
		    switch(iCh) {
			case 0: data.id = IXISA_IDIO_A;	break;
			case 1: data.id = IXISA_IDIO_B;	break;
			case 2: data.id = IXISA_DIO_A;	break;
			case 3: data.id = IXISA_DIO_B;	break;
		    }
		else switch(iCh) {
		    case 0: data.id = IXISA_DIO_A;	break;
		    case 1: data.id = IXISA_DIO_B;	break;
		    case 2: data.id = IXISA_DIO_C;	break;
		    case 3: data.id = IXISA_DIO_D;	break;
		}
		break;
	}
	data.value = 0;
	for(int iO = 7; iO >= 0; iO--) {
	    data.value = data.value << 1;
	    if(p->vlAt(TSYS::strMess("o%d_%d",iCh,iO)).at().getB(0, true)) data.value |= 1;
	}
	data.value ^= p->dInOutRev[iCh+(ePrm->dev.DI&0xFF)];
	ioctl(ePrm->devFd, IXISA_WRITE_REG, &data);
    }
}

bool da_ISA::cntrCmdProc( TMdPrm *p, XMLNode *opt )
{
    DevFeature dev = devs[modType(p,p->modTp.getS())];

    if(opt->name() == "info") {
	if((dev.AI || dev.DIO || dev.DI || dev.DO) && p->ctrMkNode3("area",opt,-1,"/cfg",_("Configuration"),SEC_RD)) {
	    // AI processing
	    if(dev.AI) {
		p->ctrMkNode3("fld",opt,-1,"/cfg/aiTm",_("Analog input settle timeout (us)"),SEC_RD|(p->enableStat()?0:SEC_WR),
		    "tp","dec", "min","0", "max","1000000");
		for(unsigned iCh = 0; iCh < (dev.AI&0xFF); iCh++)
		    p->ctrMkNode3("fld",opt,-1,TSYS::strMess("/cfg/aiG%d",iCh).c_str(),TSYS::strMess(_("AI %d gain"),iCh).c_str(),
			SEC_RD|(p->enableStat()?0:SEC_WR), "tp","dec", "min","0", "max","100");
	    }

	    // DIO processing
	    for(unsigned iCh = 0; iCh < dev.DIO; iCh++) {
		p->ctrMkNode3("fld",opt,-1,TSYS::strMess("/cfg/chnOut%d",iCh).c_str(),TSYS::strMess(_("Channel %d out"),iCh).c_str(),
		    SEC_RD|(p->enableStat()?0:SEC_WR), "tp","bool");
		for(unsigned iN = 0; iN < 24; iN++)
		    p->ctrMkNode3("fld",opt,-1,TSYS::strMess("/cfg/nRevs%d_%d",iCh,iN).c_str(),
			(iN==0)?TSYS::strMess(_("IO %d reverse"),iCh).c_str():"",
			SEC_RD|(p->enableStat()?0:SEC_WR), "tp","bool");
	    }

	    // DI and DO processing
	    if(dev.DI || dev.DO) {
		for(unsigned iCh = 0; iCh < ((dev.DI&0xFF)+(dev.DO&0xFF)); iCh++)
		    for(unsigned iN = 0; iN < 8; iN++)
			p->ctrMkNode3("fld",opt,-1,TSYS::strMess("/cfg/nRevs%d_%d",iCh,iN).c_str(), (iN==0) ?
			    ((iCh < (dev.DI&0xFF)) ? TSYS::strMess(_("DI %d reverse"),iCh).c_str() :
						      TSYS::strMess(_("DO %d reverse"),iCh-(dev.DI&0xFF)).c_str()) : "",
			    SEC_RD|(p->enableStat()?0:SEC_WR), "tp","bool");
	    }
	}
	return true;
    }
    //Process command to page
    string a_path = opt->attr("path");
    // AI processing
    if(dev.AI) {
	if(a_path == "/cfg/aiTm") {
	    if(p->ctrChkNode2(opt,"get",SEC_RD)) opt->setText(i2s(s2i(p->modPrm("aiTm","200"))));
	    if(p->ctrChkNode2(opt,"set",SEC_WR)) p->setModPrm("aiTm",opt->text());
	}
	else if(a_path.compare(0,8,"/cfg/aiG") == 0) {
	    if(p->ctrChkNode2(opt,"get",SEC_RD)) opt->setText(i2s(s2i(p->modPrm("aiGain."+a_path.substr(8)))));
	    if(p->ctrChkNode2(opt,"set",SEC_WR)) p->setModPrm("aiGain."+a_path.substr(8),opt->text());
	}
	else return false;
    }
    //DIO, DI and DO processing
    else if(dev.DIO || dev.DI || dev.DO) {
	if(a_path.compare(0,11,"/cfg/chnOut") == 0) {
	    int rout = atoi(a_path.c_str()+11);
	    if(p->ctrChkNode2(opt,"get",SEC_RD)) opt->setText(s2i(p->modPrm("DirectDIO"))&(1<<rout)?"1":"0");
	    if(p->ctrChkNode2(opt,"set",SEC_WR))
		p->setModPrm("DirectDIO",i2s(s2i(opt->text()) ? s2i(p->modPrm("DirectDIO")) | (1<<rout) :
								s2i(p->modPrm("DirectDIO")) & ~(1<<rout)));
	}
	else if(a_path.compare(0,10,"/cfg/nRevs") == 0) {
	    int iCh = 0, iN = 0;
	    sscanf(a_path.c_str(),"/cfg/nRevs%d_%d",&iCh,&iN);
	    int chVl = s2i(p->modPrm("dIORev"+i2s(iCh)));
	    if(p->ctrChkNode2(opt,"get",SEC_RD)) opt->setText((chVl&(1<<iN))?"1":"0");
	    if(p->ctrChkNode2(opt,"set",SEC_WR))
		p->setModPrm("dIORev"+i2s(iCh), i2s(s2i(opt->text()) ? (chVl|(1<<iN)) : (chVl & ~(1<<iN))));
	}
	else return false;
    }
    else return false;

    return true;
}
