--
StephanLinn - 29 Jun 2009
The 'Idiots Guide' to PVSS Control Language Extensions
First lets document Sasha's notes.
Installation steps:
1. Install PVSS with API
2. Install and configure visual studio
3. In addition to PVSS environment variables, we need $API_ROOT = c:\etm\PVSS2\3.X\api
4. Add the IDE executable library to PATH???
5. Copy vsvars.bat to c:\etm\PVSS2\3.X\api
Compilation:
In command promt
>cd c:\etm\PVSS2\3.X\api
>vsvars32
>checkAPIenv
>devenv
To Create a new CtrlExt:
A CLE is a compiled piece of code that can be executed by PVSS script. A example is given with helpful TODO comments in "$API_ROOT\TemplateCtrlExt\_TEMPLATE_ExternHdl.cxx:
static FunctionListRec fnList[] =
{
// TODO add for every new function an entry
{ INTEGER_VAR, "add", "(int a, int b, int &sum)", false }
};
.......
// TODO implement your logic; here: get first and second argument
// and convert them to IntegerVar (Variable::operator= does autmatic casting)
IntegerVar a, b;
a = *(param.args->getFirst()->evaluate(param.thread));
b = *(param.args->getNext ()->evaluate(param.thread));
.....
// calculate the sum
integerVar.setValue(a.getValue() + b.getValue());
....
// This shows how to provide data to an in/out parameter
// get a pointer to the given reference variable
Variable *sum = param.args->getNext()->getTarget(param.thread);
This is nice if you happen to be arithmetically challenged or only want to pass single integers in and out. If you are interested in passing anything more complex like strings, blobs or dynamic arrays, the documentation is scarce. That was until I found our very own Fernando Varela-Rodriguez Ph.D.
thesis
, which contained enough hints to get started.
Below is the HCAL DSS CLE for control of the 1-wire ADC. It merely encapsulates some routines that use the "TMEX" api from Dallas Semiconductor. It shows how to pass uchar arrays and blobs into out of PVSS and how to use dynamic arrays internally. Hopefully someone will do a better and more complete job. I don't have time now.
#include <HssExternHdl.hxx>
#include "BlobVar.hxx"
#include "CharVar.hxx"
#include "CtrlVar.hxx"
#include "TextVar.hxx"
#include "DynVar.hxx"
#include "atod.h"
#include <iostream>
#include <iomanip>
#include <string>
//============================================================================================================
static FunctionListRec fnList[] =
{
{ INTEGER_VAR, "TestPVSS" , "(int verbose", false },
{ INTEGER_VAR, "ReadAtoDResultsPVSS" , "(int portnum, unsigned char * SerialNum, blob &results)", false },
{ INTEGER_VAR, "FindDevicePVSS" , "(int portnum, int devType, blob &FamilySN)", false },
{ INTEGER_VAR, "WriteAtoDPVSS" , "(int portnum, unsigned char * SerialNum, unsigned char * ctrl )",false },
{ INTEGER_VAR, "DoAtoDConversionPVSS", "(int portnum, unsigned char * SerialNum)",false},
{ INTEGER_VAR, "ReleasePVSS" , "(int portnum )",false},
{ INTEGER_VAR, "AcquirePVSS" , "(char * portName)",false},
{ INTEGER_VAR, "SelectPVSS" , "(int portnum)",false}
};
CTRL_EXTENSION(HssExternHdl, fnList)
//============================================================================================================
const Variable *HssExternHdl::execute(ExecuteParamRec ¶m)
{
enum
{
// TODO add here all your function numbers in the sequence used in the FunctionListRec array
F_TestPVSS=0,F_ReadAtoDResultsPVSS, F_FindDevicePVSS, F_WriteAtoDPVSS, F_DoAtoDConversionPVSS,F_ReleasePVSS,F_AcquirePVSS,F_SelectPVSS
};
//============================================================================================================
// prints to file: 0( none ) 1( debug )
static int VERBOSE=0;
unsigned char uadd[16],uctl[16];
// TODO for the different return types you need corresponding static Variables
static IntegerVar integerVar;
static BlobVar blobVar;
// pointers used in all
CtrlExpr *expr;
const Variable *numPtr;
const Variable *devPtr;
const Variable *intPtr;
switch ( param.funcNum )
{
//============================================================================================================
case F_TestPVSS:
{
param.thread->clearLastError();
// check number of arguments
if ( !param.args || (param.args->getNumberOfItems() != 1) ){
integerVar.setValue(-1); return &integerVar;
}
expr = param.args->getFirst();
numPtr = expr -> evaluate(param.thread);
if ( ! numPtr ){
integerVar.setValue(-1); return &integerVar;
}
IntegerVar portNum;
portNum = *numPtr;
VERBOSE = portNum.getValue();
//if (VERBOSE > 0){
// std::freopen("AAAlogfile.txt","a",stderr);
//}
//else {
// fclose(stderr);
//}
if (VERBOSE >0)std::cout << "\nTestPVSS: portnum "<<std::dec<<portNum.getValue()<<std::endl;
integerVar.setValue( 0 ) ;return &integerVar;
}
//============================================================================================================
case F_FindDevicePVSS:
{
param.thread->clearLastError();
// check number of arguments
// TODO
if ( !param.args || (param.args->getNumberOfItems() != 3) )
{
ErrClass err(ErrClass::PRIO_WARNING,
ErrClass::ERR_CONTROL, ErrClass::ARG_MISSING,
param.clientData->getLocation(), param.funcName);
ErrHdl::error(err);
param.thread->appendLastError(&err);
integerVar.setValue(-1); return &integerVar;
}
// get portnum
expr = param.args->getFirst();
numPtr = expr -> evaluate(param.thread);
if ( ! numPtr ){
integerVar.setValue(-1); return &integerVar;
}
IntegerVar portNum;
portNum = *numPtr;
// get devType
expr = param.args->getNext();
devPtr = expr -> evaluate(param.thread);
if ( ! devPtr ){
integerVar.setValue(-1); return &integerVar;
}
IntegerVar devType;
devType = *devPtr;
// This shows how to provide data to an in/out parameter
// get a pointer to the given reference variable
Variable *sum = param.args->getNext()->getTarget(param.thread);
if ( ! sum )
{
ErrClass err(ErrClass::PRIO_WARNING,
ErrClass::ERR_CONTROL, ErrClass::NO_LVALUE,
param.clientData->getLocation(), param.funcName);
ErrHdl::error(err);
param.thread->appendLastError(&err);
integerVar.setValue(-1); return &integerVar;
}
// get FamilySN into PVSSuchar?
PVSSuchar FamilySN[16][8];
int nFound = FindDevice(portNum.getValue(), &FamilySN[0], devType.getValue(), MAXDEVICES);
if (VERBOSE > 0){
std::cout << "FindDevicePVSS: portnum "<< std::dec<< portNum.getValue()<<" nFound "<<nFound <<" adapID "<<std::hex<<devType.getValue()<<std::endl;
for ( int i = 0; i < nFound; i++){
for ( int j=0; j < 8; j++ ){
std::cout<< std::hex<<std::setw(2)<<std::setfill('0')<<(int)FamilySN[i][j];
}
std::cout<<std::endl;
}
}
// directly assign the result to the target var
//static BlobVar blobVar( temp, sizeof(temp), PVSS_TRUE );
//blobVar = BlobVar( temp, sizeof(temp), PVSS_TRUE );
blobVar = BlobVar( FamilySN[0], nFound*8 , PVSS_TRUE );
*sum = blobVar;
integerVar.setValue(nFound); return &integerVar;
}
//============================================================================================================
case F_ReadAtoDResultsPVSS:
{
param.thread->clearLastError();
// check number of arguments
if ( !param.args || (param.args->getNumberOfItems() != 3) ){
integerVar.setValue(-1); return &integerVar;
}
// get portnum
expr = param.args->getFirst();
numPtr = expr -> evaluate(param.thread);
if ( ! numPtr ){
integerVar.setValue(-1); return &integerVar;
}
IntegerVar portNum;
portNum = *numPtr;
// get chipID blob
TextVar chipPtr;
chipPtr = *(param.args->getNext()->evaluate(param.thread));
if ( ! chipPtr ){
integerVar.setValue(-1); return &integerVar;
}
const char * add;
add = new char[32];
add = chipPtr.getValue(); // gets the string
sscanf( add, "%2x %2x %2x %2x %2x %2x %2x %2x ",
&uadd[0],&uadd[1],&uadd[2],&uadd[3],&uadd[4],&uadd[5],&uadd[6],&uadd[7] );
delete[] add;
add = NULL;
int adc[4];
int status = ReadAtoDResults(portNum.getValue(), TRY_OVERDRIVE, uadd, &adc[0]);
if (VERBOSE > 0){
std::cout << "ReadAtoDPVSS : portnum "<<std::dec<<portNum.getValue()<<" status "<<status <<" chip ";
for (int i=0;i<8;i++)std::cout<< std::hex<<std::setw(2)<<std::setfill('0')<<(int)uadd[i];
std::cout<< std::dec<< " adc "<<adc[0]<<" "<<adc[1]<<" "<<adc[2]<<" "<<adc[3]<< std::endl;
}
// output dyn_int unsigned char * dum;
expr = param.args->getNext();
intPtr = expr -> getTarget(param.thread);
if ( ! intPtr ){
integerVar.setValue(-1); return &integerVar;
}
((DynVar *) intPtr)->clear();
for(int i=0;i<4;i++){
//((DynVar *) intPtr)->append(new IntegerVar(adc[i]));
((DynVar *)intPtr)->append( IntegerVar(adc[i]) );
}
integerVar.setValue(0); return &integerVar;
}
//============================================================================================================
case F_WriteAtoDPVSS:
{
param.thread->clearLastError();
// check number of arguments
// TODO
if ( !param.args || (param.args->getNumberOfItems() != 3) )
{
ErrClass err(ErrClass::PRIO_WARNING,
ErrClass::ERR_CONTROL, ErrClass::ARG_MISSING,
param.clientData->getLocation(), param.funcName);
ErrHdl::error(err);
param.thread->appendLastError(&err);
integerVar.setValue(-1); return &integerVar;
}
// get portnum
expr = param.args->getFirst();
numPtr = expr -> evaluate(param.thread);
if ( ! numPtr ){
integerVar.setValue(-1); return &integerVar;
}
IntegerVar portNum;
portNum = *numPtr;
// get chipID blob
TextVar chipPtr;
chipPtr = *(param.args->getNext()->evaluate(param.thread));
if ( ! chipPtr ){
integerVar.setValue(-1); return &integerVar;
}
const char * add;
add = new char[32];
add = chipPtr.getValue(); // gets the string
sscanf( add, "%2x %2x %2x %2x %2x %2x %2x %2x ",
&uadd[0],&uadd[1],&uadd[2],&uadd[3],&uadd[4],&uadd[5],&uadd[6],&uadd[7] );
delete[] add;
add = NULL;
// get ctrl blob
TextVar ctrlPtr;
ctrlPtr = *(param.args->getNext()->evaluate(param.thread));
if ( ! ctrlPtr ){
integerVar.setValue(-1); return &integerVar;
}
const char * ctl;
ctl = new char[32];
ctl = ctrlPtr.getValue(); // gets the string
sscanf( ctl, "%2x %2x %2x %2x %2x %2x %2x %2x ",
&uctl[0],&uctl[1],&uctl[2],&uctl[3],&uctl[4],&uctl[5],&uctl[6],&uctl[7] );
delete[] ctl;
ctl = NULL;
int status = WriteAtoD(portNum.getValue(),TRY_OVERDRIVE, uadd, uctl, START_ADD, END_ADD);
if (VERBOSE > 0){
std::cout << "\nWriteAtoDPVSS: portnum "<<std::dec<<portNum.getValue()<<" status "<< status<<" chip ";
for (int i=0;i<8;i++)std::cout<< std::hex<<std::setw(2)<<std::setfill('0')<<(int)uadd[i];
std::cout<<" ctrl ";
for (int i=0;i<8;i++)std::cout<< std::hex<<std::setw(2)<<std::setfill('0')<<(int)uctl[i];
std::cout<< std::endl;
}
uctl[0] = 0x40;
int vccAdd = 0x1C;
status = WriteAtoD(portNum.getValue(),TRY_OVERDRIVE, uadd, uctl, vccAdd, vccAdd);
// directly assign the result to the target var
integerVar.setValue(status); return &integerVar;
}
//============================================================================================================
case F_DoAtoDConversionPVSS:
{
param.thread->clearLastError();
// check number of arguments
// TODO
if ( !param.args || (param.args->getNumberOfItems() != 2) )
{
ErrClass err(ErrClass::PRIO_WARNING,
ErrClass::ERR_CONTROL, ErrClass::ARG_MISSING,
param.clientData->getLocation(), param.funcName);
ErrHdl::error(err);
param.thread->appendLastError(&err);
integerVar.setValue(-1); return &integerVar;
}
// get portnum
expr = param.args->getFirst();
numPtr = expr -> evaluate(param.thread);
if ( ! numPtr ){
integerVar.setValue(-1); return &integerVar;
}
IntegerVar portNum;
portNum = *numPtr;
// get chipID blob
TextVar chipPtr;
chipPtr = *(param.args->getNext()->evaluate(param.thread));
if ( ! chipPtr ){
integerVar.setValue(-1); return &integerVar;
}
const char * add;
add = new char[32];
add = chipPtr.getValue(); // gets the string
sscanf( add, "%2x %2x %2x %2x %2x %2x %2x %2x ",
&uadd[0],&uadd[1],&uadd[2],&uadd[3],&uadd[4],&uadd[5],&uadd[6],&uadd[7] );
delete[] add;
add = NULL;
int status = DoAtoDConversion(portNum.getValue(),TRY_OVERDRIVE, uadd);
if (VERBOSE > 0){
std::cout << "DoAtoDPVSS : portnum "<<std::dec<<portNum.getValue()<<" status "<<status <<" chip ";
for (int i=0;i<8;i++)std::cout<< std::hex<<std::setw(2)<<std::setfill('0')<<(int)uadd[i];
std::cout<< std::endl;
}
// directly assign the result to the target var
integerVar.setValue(status); return &integerVar;
}
//============================================================================================================
case F_ReleasePVSS:
{
param.thread->clearLastError();
// check number of arguments
// TODO
if ( !param.args || (param.args->getNumberOfItems() != 1) )
{
ErrClass err(ErrClass::PRIO_WARNING,
ErrClass::ERR_CONTROL, ErrClass::ARG_MISSING,
param.clientData->getLocation(), param.funcName);
ErrHdl::error(err);
param.thread->appendLastError(&err);
integerVar.setValue(-1); return &integerVar;
}
// get portnum
expr = param.args->getFirst();
numPtr = expr -> evaluate(param.thread);
if ( ! numPtr ){
integerVar.setValue(-1); return &integerVar;
}
IntegerVar portNum;
portNum = *numPtr;
int status = Release(portNum.getValue());
if (VERBOSE > 0)std::cout << "ReleasePVSS: portnum "<<std::dec<< portNum.getValue() <<" status "<< status<< std::endl;
// directly assign the result to the target var
integerVar.setValue(status); return &integerVar;
}
//============================================================================================================
case F_AcquirePVSS:
{
param.thread->clearLastError();
// check number of arguments
// TODO
if ( !param.args || (param.args->getNumberOfItems() != 1) )
{
ErrClass err(ErrClass::PRIO_WARNING,
ErrClass::ERR_CONTROL, ErrClass::ARG_MISSING,
param.clientData->getLocation(), param.funcName);
ErrHdl::error(err);
param.thread->appendLastError(&err);
integerVar.setValue(-1); return &integerVar;
}
// TODO implement your logic; here: get first and second argument
// and convert them to IntegerVar (Variable::operator= does autmatic casting)
TextVar portNamePtr;
portNamePtr = *(param.args->getFirst()->evaluate(param.thread));
char * portName = (char*)portNamePtr.getString();
int portNum = Acquire((char*)portNamePtr.getString());
if (VERBOSE > 0)std::cout << "\nAcquirePVSS: portnum "<<std::dec<< portNum <<" portName "<< portName<< std::endl;
// directly assign the result to the target var
integerVar.setValue(portNum); return &integerVar;
}
//============================================================================================================
case F_SelectPVSS:
{
param.thread->clearLastError();
// check number of arguments
// TODO
if ( !param.args || (param.args->getNumberOfItems() != 1) )
{
ErrClass err(ErrClass::PRIO_WARNING,
ErrClass::ERR_CONTROL, ErrClass::ARG_MISSING,
param.clientData->getLocation(), param.funcName);
ErrHdl::error(err);
param.thread->appendLastError(&err);
integerVar.setValue(-1); return &integerVar;
}
// get portnum
expr = param.args->getFirst();
numPtr = expr -> evaluate(param.thread);
if ( ! numPtr ){
integerVar.setValue(-1); return &integerVar;
}
IntegerVar portNum;
portNum = *numPtr;
int status = Select(portNum.getValue(),TRY_OVERDRIVE);
if (VERBOSE > 0)std::cout << "SelectPVSS: portnum "<<std::dec<< portNum.getValue() <<" status "<< status<< std::endl;
// directly assign the result to the target var
integerVar.setValue(status); return &integerVar;
}
//============================================================================================================
default:
integerVar.setValue(-1);
return &integerVar;
}
}