diff --git a/pvAccessApp/Makefile b/pvAccessApp/Makefile index 286276c..01422d6 100644 --- a/pvAccessApp/Makefile +++ b/pvAccessApp/Makefile @@ -17,18 +17,22 @@ INC += arrayFIFO.h INC += growingCircularBuffer.h INC += inetAddressUtil.h INC += logger.h +INC += introspectionRegistry.h LIBSRCS += hexDump.cpp LIBSRCS += wildcharMatcher.cpp LIBSRCS += inetAddressUtil.cpp LIBSRCS += logger.cpp +LIBSRCS += introspectionRegistry.cpp SRC_DIRS += $(PVACCESS)/client INC += pvAccess.h +LIBSRCS += pvAccess.cpp SRC_DIRS += $(PVACCESS)/factory LIBSRCS += ChannelAccessFactory.cpp +LIBSRCS += CreateRequestFactory.cpp SRC_DIRS += $(PVACCESS)/remote diff --git a/pvAccessApp/client/pvAccess.cpp b/pvAccessApp/client/pvAccess.cpp new file mode 100644 index 0000000..dc66264 --- /dev/null +++ b/pvAccessApp/client/pvAccess.cpp @@ -0,0 +1,13 @@ +/*pvAccess.cpp*/ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvDataCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +#include + +namespace epics { namespace pvAccess { + +const char* ConnectionStateNames[] = { "NEVER_CONNECTED", "CONNECTED", "DISCONNECTED", "DESTROYED" }; + +}} \ No newline at end of file diff --git a/pvAccessApp/client/pvAccess.h b/pvAccessApp/client/pvAccess.h index c85a9da..da230c3 100644 --- a/pvAccessApp/client/pvAccess.h +++ b/pvAccessApp/client/pvAccess.h @@ -33,7 +33,7 @@ namespace epics { namespace pvAccess { NEVER_CONNECTED, CONNECTED, DISCONNECTED, DESTROYED }; - const char* ConnectionStateNames[] = { "NEVER_CONNECTED", "CONNECTED", "DISCONNECTED", "DESTROYED" }; + extern const char* ConnectionStateNames[]; class Channel; @@ -714,6 +714,27 @@ namespace epics { namespace pvAccess { }; + /** + * Interface for creating request structure. + * @author mse + * + */ + class CreateRequest : private epics::pvData::NoDefaultMethods { + public: + virtual ~CreateRequest() {}; + + /** + * Create a request structure for the create calls in Channel. + * See the package overview documentation for details. + * @param request The field request. See the package overview documentation for details. + * @param requester The requester; + * @return The request structure if an invalid request was given. + */ + virtual epics::pvData::PVStructure* createRequest(String request, epics::pvData::Requester* requester) = 0; + }; + + extern CreateRequest * getCreateRequest(); + }} diff --git a/pvAccessApp/factory/ChannelAccessFactory.cpp b/pvAccessApp/factory/ChannelAccessFactory.cpp index f3dbfe7..c474353 100644 --- a/pvAccessApp/factory/ChannelAccessFactory.cpp +++ b/pvAccessApp/factory/ChannelAccessFactory.cpp @@ -2,9 +2,9 @@ #include #include -#include "pvAccess.h" -#include "pvData.h" -#include "factory.h" +#include +#include +#include #include #include diff --git a/pvAccessApp/factory/CreateRequestFactory.cpp b/pvAccessApp/factory/CreateRequestFactory.cpp new file mode 100644 index 0000000..ce9e05d --- /dev/null +++ b/pvAccessApp/factory/CreateRequestFactory.cpp @@ -0,0 +1,263 @@ +/*CreateRequestFactory.cpp*/ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvDataCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +#include +#include +#include +#include +#include + +using namespace epics::pvData; + +namespace epics { namespace pvAccess { + +class CreateRequestImpl : public CreateRequest { + private: + + static void trim(std::string& str) + { + std::string::size_type pos = str.find_last_not_of(' '); + if(pos != std::string::npos) { + str.erase(pos + 1); + pos = str.find_first_not_of(' '); + if(pos != std::string::npos) str.erase(0, pos); + } + else str.erase(str.begin(), str.end()); + } + + static int findMatchingBrace(std::string& request, int index, int numOpen) { + size_t openBrace = request.find('{', index+1); + size_t closeBrace = request.find('}', index+1); + if(openBrace == std::string::npos && closeBrace == std::string::npos) return std::string::npos; + if (openBrace != std::string::npos) { + if(openBraceopenBrace)) { + //find matching brace + size_t closeBrace = findMatchingBrace(request,openBrace+1,1); + if(closeBrace==std::string::npos) { + requester->message(request + "mismatched { }", errorMessage); + return false; + } + String fieldName = request.substr(0,openBrace); + PVStructure* pvStructure = getPVDataCreate()->createPVStructure(pvParent, fieldName, 0); + createFieldRequest(pvStructure,request.substr(openBrace+1,closeBrace-openBrace-1),false,requester); + // error check? + pvParent->appendPVField(pvStructure); + if(request.length()>closeBrace+1) { + if(request.at(closeBrace+1) != ',') { + requester->message(request + "misssing , after }", errorMessage); + return false; + } + if(!createFieldRequest(pvParent,request.substr(closeBrace+2),false,requester)) return false; + } + return true; + } + if(openBracket==std::string::npos && fieldListOK) { + PVString* pvStringField = static_cast(getPVDataCreate()->createPVScalar(pvParent, "fieldList", pvString)); + pvStringField->put(request); + pvParent->appendPVField(pvStringField); + return true; + } + if(openBracket!=std::string::npos && (comma==std::string::npos || comma>openBracket)) { + size_t closeBracket = request.find(']'); + if(closeBracket==std::string::npos) { + requester->message(request + "option does not have matching []", errorMessage); + return false; + } + if(!createLeafFieldRequest(pvParent,request.substr(0, closeBracket+1),requester)) return false; + if(request.rfind(',')>closeBracket) { + int nextComma = request.find(',', closeBracket); + if(!createFieldRequest(pvParent,request.substr(nextComma+1),false,requester)) return false; + } + return true; + } + if(comma!=std::string::npos) { + if(!createLeafFieldRequest(pvParent,request.substr(0, comma),requester)) return false; + return createFieldRequest(pvParent,request.substr(comma+1),false,requester); + } + return createLeafFieldRequest(pvParent,request,requester); + } + + static bool createLeafFieldRequest(PVStructure* pvParent,String request,Requester* requester) { + size_t openBracket = request.find('['); + String fullName = request; + if(openBracket != std::string::npos) fullName = request.substr(0,openBracket); + size_t indLast = fullName.rfind('.'); + String fieldName = fullName; + if(indLast>1 && indLast != std::string::npos) fieldName = fullName.substr(indLast+1); + PVStructure* pvStructure = getPVDataCreate()->createPVStructure(pvParent, fieldName, 0); + PVStructure* pvLeaf = getPVDataCreate()->createPVStructure(pvStructure,"leaf", 0); + PVString* pvStringField = static_cast(getPVDataCreate()->createPVScalar(pvLeaf, "source", pvString)); + pvStringField->put(fullName); + pvLeaf->appendPVField(pvStringField); + if(openBracket != std::string::npos) { + size_t closeBracket = request.find(']'); + if(closeBracket==std::string::npos) { + delete pvLeaf; + delete pvStructure; + requester->message("option does not have matching []", errorMessage); + return false; + } + if(!createRequestOptions(pvLeaf,request.substr(openBracket+1, closeBracket-openBracket-1),requester)) + { + delete pvLeaf; + delete pvStructure; + return false; + } + } + pvStructure->appendPVField(pvLeaf); + pvParent->appendPVField(pvStructure); + return true; + } + + static bool createRequestOptions(PVStructure* pvParent,std::string request,Requester* requester) { + trim(request); + if(request.length()<=1) return true; + + std::string token; + std::istringstream iss(request); + while (getline(iss, token, ',')) + { + size_t equalsPos = token.find('='); + size_t equalsRPos = token.rfind('='); + if (equalsPos != equalsRPos) + { + requester->message("illegal option", errorMessage); + return false; + } + + PVString* pvStringField = static_cast(getPVDataCreate()->createPVScalar(pvParent, token.substr(0, equalsPos), pvString)); + pvStringField->put(token.substr(equalsPos+1)); + pvParent->appendPVField(pvStringField); + } + return true; + } + + public: + + virtual PVStructure* createRequest(String request, Requester* requester) + { + static String emptyString; + if (!request.empty()) trim(request); + if (request.empty()) + { + return getPVDataCreate()->createPVStructure(0, emptyString, 0); + } + + size_t offsetRecord = request.find("record["); + size_t offsetField = request.find("field("); + size_t offsetPutField = request.find("putField("); + size_t offsetGetField = request.find("getField("); + + PVStructure* pvStructure = getPVDataCreate()->createPVStructure(0, emptyString, 0); + if (offsetRecord != std::string::npos) { + size_t offsetBegin = request.find('[', offsetRecord); + size_t offsetEnd = request.find(']', offsetBegin); + if(offsetEnd == std::string::npos) { + delete pvStructure; + requester->message("record[ does not have matching ]", errorMessage); + return 0; + } + PVStructure* pvStruct = getPVDataCreate()->createPVStructure(pvStructure, "record", 0); + if(!createRequestOptions(pvStruct,request.substr(offsetBegin+1, offsetEnd-offsetBegin-1),requester)) + { + delete pvStruct; + delete pvStructure; + return 0; + } + pvStructure->appendPVField(pvStruct); + } + if (offsetField != std::string::npos) { + size_t offsetBegin = request.find('(', offsetField); + size_t offsetEnd = request.find(')', offsetBegin); + if(offsetEnd == std::string::npos) { + delete pvStructure; + requester->message("field( does not have matching )", errorMessage); + return 0; + } + PVStructure* pvStruct = getPVDataCreate()->createPVStructure(pvStructure, "field", 0); + if(!createFieldRequest(pvStruct,request.substr(offsetBegin+1, offsetEnd-offsetBegin-1),true,requester)) + { + delete pvStruct; + delete pvStructure; + return 0; + } + pvStructure->appendPVField(pvStruct); + } + if (offsetPutField != std::string::npos) { + size_t offsetBegin = request.find('(', offsetPutField); + size_t offsetEnd = request.find(')', offsetBegin); + if(offsetEnd == std::string::npos) { + delete pvStructure; + requester->message("putField( does not have matching )", errorMessage); + return 0; + } + PVStructure* pvStruct = getPVDataCreate()->createPVStructure(pvStructure, "putField", 0); + if(!createFieldRequest(pvStruct,request.substr(offsetBegin+1, offsetEnd-offsetBegin-1),true,requester)) + { + delete pvStruct; + delete pvStructure; + return 0; + } + pvStructure->appendPVField(pvStruct); + } + if (offsetGetField != std::string::npos) { + size_t offsetBegin = request.find('(', offsetGetField); + size_t offsetEnd = request.find(')', offsetBegin); + if(offsetEnd == std::string::npos) { + delete pvStructure; + requester->message("getField( does not have matching )", errorMessage); + return 0; + } + PVStructure* pvStruct = getPVDataCreate()->createPVStructure(pvStructure, "getField", 0); + if(!createFieldRequest(pvStruct,request.substr(offsetBegin+1, offsetEnd-offsetBegin-1),true,requester)) + { + delete pvStruct; + delete pvStructure; + return 0; + } + pvStructure->appendPVField(pvStruct); + } + if (pvStructure->getStructure()->getNumberFields()==0) { + if(!createFieldRequest(pvStructure,request,true,requester)) + { + delete pvStructure; + return 0; + } + } + return pvStructure; + } + +}; + +static CreateRequest* createRequest = 0; + +CreateRequest * getCreateRequest() { + static Mutex mutex = Mutex(); + Lock guard(&mutex); + + if(createRequest==0){ + createRequest = new CreateRequestImpl(); + } + return createRequest; +} + +}} + diff --git a/pvAccessApp/utils/introspectionRegistry.cpp b/pvAccessApp/utils/introspectionRegistry.cpp new file mode 100644 index 0000000..8b52e23 --- /dev/null +++ b/pvAccessApp/utils/introspectionRegistry.cpp @@ -0,0 +1,476 @@ +/* + * introspectionRegistry.cpp + */ + +#include "introspectionRegistry.h" + +namespace epics { namespace pvAccess { + +const int8 IntrospectionRegistry::NULL_TYPE_CODE = (int8)-1; +const int8 IntrospectionRegistry::ONLY_ID_TYPE_CODE = (int8)-2; +const int8 IntrospectionRegistry::FULL_WITH_ID_TYPE_CODE = (int8)-3; +PVDataCreate* IntrospectionRegistry::_pvDataCreate = getPVDataCreate(); +StatusCreate* IntrospectionRegistry::_statusCreate = getStatusCreate(); +FieldCreate* IntrospectionRegistry::_fieldCreate = getFieldCreate(); + +IntrospectionRegistry::IntrospectionRegistry(bool serverSide) : _mutex(Mutex()) +{ + + _direction = serverSide ? 1 : -1; + reset(); +} + +IntrospectionRegistry::~IntrospectionRegistry() +{ +} + +void IntrospectionRegistry::reset() +{ + Lock guard(&_mutex); + _outgoingIdPointer = _direction; + //decrement references + for(_registryRIter = _registry.rbegin(); _registryRIter != _registry.rend(); _registryRIter++) + { + _registryRIter->second->decReferenceCount(); + } + _registry.clear(); +} + +FieldConstPtr IntrospectionRegistry::getIntrospectionInterface(const short id) +{ + Lock guard(&_mutex); + _registryIter = _registry.find(id); + if(_registryIter == _registry.end()) + { + throw EpicsException("missing interface with given id"); + } + return _registryIter->second; +} + +void IntrospectionRegistry::registerIntrospectionInterface(const short id,FieldConstPtr field) +{ + Lock guard(&_mutex); + //first decrement reference on old value + _registryIter = _registry.find(id); + if(_registryIter != _registry.end()) + { + _registryIter->second->decReferenceCount(); + } + _registry[id] = field; + field->incReferenceCount(); +} + +short IntrospectionRegistry::registerIntrospectionInterface(FieldConstPtr field, bool& existing) +{ + Lock guard(&_mutex); + short key; + if(registryContainsValue(field, key)) + { + existing = true; + } + else + { + existing = false; + key = _outgoingIdPointer; + _outgoingIdPointer += _direction; + // wrap check + if(_outgoingIdPointer * _direction < 0) + { + _outgoingIdPointer = _direction; + } + + //first decrement reference on old value + _registryIter = _registry.find(key); + if(_registryIter != _registry.end()) + { + cout << "deleting 2" << endl; + _registryIter->second->decReferenceCount(); + } + + _registry[key] = field; + field->incReferenceCount(); + } + return key; +} + +void IntrospectionRegistry::printKeysAndValues(string name) +{ + string buffer; + cout << "############## print of all key/values of " << name.c_str() << " registry : ###################" << endl; + for(_registryIter = _registry.begin(); _registryIter != _registry.end(); _registryIter++) + { + buffer.clear(); + cout << "\t" << "Key: "<< _registryIter->first << endl; + cout << "\t" << "Value: " << _registryIter->second << endl; + _registryIter->second->toString(&buffer); + cout << "\t" << "Value toString: " << buffer.c_str() << endl; + } +} + +bool IntrospectionRegistry::registryContainsValue(FieldConstPtr field, short& key) +{ + for(_registryRIter = _registry.rbegin(); _registryRIter != _registry.rend(); _registryRIter++) + { + if(compareFields(field,_registryRIter->second)) + { + key = _registryRIter->first; + return true; + } + } + return false; +} + +bool IntrospectionRegistry::compareFields(FieldConstPtr field1, FieldConstPtr field2) +{ + if(field1->getFieldName() != field2->getFieldName()) + { + return false; + } + else + { + Type typ1 = field1->getType(); + Type typ2 = field2->getType(); + if(typ1 != typ2) + { + return false; + } + + switch (typ1) + { + case epics::pvData::scalar: + { + if(static_cast(field1)->getScalarType() == static_cast(field1)->getScalarType()) + { + return true; + } + break; + } + case epics::pvData::scalarArray: + { + if(static_cast(field1)->getElementType() == static_cast(field1)->getElementType()) + { + return true; + } + break; + } + case epics::pvData::structure: + { + return true; + break; + } + case epics::pvData::structureArray: + { + if(static_cast(field1)->getStructure()->getFieldName() == static_cast(field1)->getStructure()->getFieldName()) + { + return true; + } + break; + } + } + return false; + } +} + +void IntrospectionRegistry::serialize(FieldConstPtr field, ByteBuffer* buffer, SerializableControl* control) +{ + checkBufferAndSerializeControl(buffer, control); + serialize(field, NULL, buffer, control, this); +} + +FieldConstPtr IntrospectionRegistry::deserialize(ByteBuffer* buffer, DeserializableControl* control) +{ + checkBufferAndDeserializeControl(buffer, control); + return deserialize(buffer, control, this); +} + +void IntrospectionRegistry::serializeFull(FieldConstPtr field, ByteBuffer* buffer, SerializableControl* control) +{ + checkBufferAndSerializeControl(buffer, control); + serialize(field, NULL, buffer, control, NULL); +} + +FieldConstPtr IntrospectionRegistry::deserializeFull(ByteBuffer* buffer, DeserializableControl* control) +{ + checkBufferAndDeserializeControl(buffer, control); + return deserialize(buffer, control, NULL); +} + +void IntrospectionRegistry::serialize(FieldConstPtr field, StructureConstPtr parent, ByteBuffer* buffer, + SerializableControl* control, IntrospectionRegistry* registry) +{ + checkBufferAndSerializeControl(buffer, control); + if (field == NULL) + { + control->ensureBuffer(1); + buffer->putByte(IntrospectionRegistry::NULL_TYPE_CODE); + } + else + { + // use registry check + // only top IFs and structures + if (registry != NULL && (parent == NULL || field->getType() == epics::pvData::structure || field->getType() == epics::pvData::structureArray)) + { + bool existing; + const short key = registry->registerIntrospectionInterface(field, existing); + /*cout << "@@@@@@@@@" << endl; + cout << field->getFieldName() << endl; + cout << "address: " << field << endl; + cout << "existing: " << existing << endl; + cout << "key: " << key << endl;*/ + if(existing) + { + control->ensureBuffer(1+sizeof(int16)/sizeof(int8)); + buffer->putByte(ONLY_ID_TYPE_CODE); + buffer->putShort(key); + return; + } + else + { + control->ensureBuffer(1+sizeof(int16)/sizeof(int8)); + buffer->putByte(FULL_WITH_ID_TYPE_CODE); // could also be a mask + buffer->putShort(key); + } + } + + // NOTE: high nibble is field.getType() ordinal, low nibble is scalar type ordinal; -1 is null + switch (field->getType()) + { + case epics::pvData::scalar: + { + ScalarConstPtr scalar = static_cast(field); + control->ensureBuffer(1); + buffer->putByte((int8)(epics::pvData::scalar << 4 | scalar->getScalarType())); + SerializeHelper::serializeString(field->getFieldName(), buffer, control); + break; + } + case epics::pvData::scalarArray: + { + ScalarArrayConstPtr array = static_cast(field); + control->ensureBuffer(1); + buffer->putByte((int8)(epics::pvData::scalarArray << 4 | array->getElementType())); + SerializeHelper::serializeString(field->getFieldName(), buffer, control); + break; + } + case epics::pvData::structure: + { + StructureConstPtr structure = static_cast(field); + control->ensureBuffer(1); + buffer->putByte((int8)(epics::pvData::structure << 4)); + serializeStructureField(buffer, control, registry, structure); + break; + } + case epics::pvData::structureArray: + { + StructureArrayConstPtr structureArray = static_cast(field); + control->ensureBuffer(1); + buffer->putByte((int8)(epics::pvData::structureArray << 4)); + SerializeHelper::serializeString(field->getFieldName(), buffer, control); + // we also need to serialize structure field... + const Structure* structureElement = structureArray->getStructure(); + serializeStructureField(buffer, control, registry, structureElement); + break; + } + } + } +} + +void IntrospectionRegistry::serializeStructureField(ByteBuffer* buffer, SerializableControl* control, + IntrospectionRegistry* registry, StructureConstPtr structure) +{ + checkBufferAndSerializeControl(buffer, control); + if(structure == NULL) + { + throw EpicsException("null structure provided"); + } + SerializeHelper::serializeString(structure->getFieldName(), buffer, control); + FieldConstPtrArray fields = structure->getFields(); + SerializeHelper::writeSize(structure->getNumberFields(), buffer, control); + for (int i = 0; i < structure->getNumberFields(); i++) + { + serialize(fields[i], structure, buffer, control, registry); + } +} + +FieldConstPtr IntrospectionRegistry::deserialize(ByteBuffer* buffer, DeserializableControl* control, IntrospectionRegistry* registry) +{ + checkBufferAndDeserializeControl(buffer, control); + control->ensureData(1); + const int8 typeCode = buffer->getByte(); + if(typeCode == IntrospectionRegistry::NULL_TYPE_CODE) + { + return NULL; + } + else if(typeCode == IntrospectionRegistry::ONLY_ID_TYPE_CODE) + { + if (registry == NULL) + { + throw EpicsException("deserialization provided chached ID, but no registry provided"); + } + control->ensureData(sizeof(int16)/sizeof(int8)); + return registry->getIntrospectionInterface(buffer->getShort()); + } + + // could also be a mask + if(typeCode == IntrospectionRegistry::FULL_WITH_ID_TYPE_CODE) + { + if(registry == NULL) + { + throw EpicsException("deserialization provided chached ID, but no registry provided"); + } + control->ensureData(sizeof(int16)/sizeof(int8)); + const short key = buffer->getShort(); + FieldConstPtr field = deserialize(buffer, control, registry); + registry->registerIntrospectionInterface(key, field); + return field; + } + + + // high nibble means scalar/array/structure + const Type type = (Type)(typeCode >> 4); + switch (type) + { + case scalar: + { + const ScalarType scalar = (ScalarType)(typeCode & 0x0F); + const String scalarFieldName = SerializeHelper::deserializeString(buffer, control); + return static_cast(_fieldCreate->createScalar(scalarFieldName,scalar)); + } + case scalarArray: + { + const ScalarType element = (ScalarType)(typeCode & 0x0F); + const String arrayFieldName = SerializeHelper::deserializeString(buffer, control); + return static_cast(_fieldCreate->createScalarArray(arrayFieldName,element)); + } + case structure: + { + return static_cast(deserializeStructureField(buffer, control, registry)); + } + case structureArray: + { + const String structureArrayFieldName = SerializeHelper::deserializeString(buffer, control); + const StructureConstPtr arrayElement = deserializeStructureField(buffer, control, registry); + return static_cast(_fieldCreate->createStructureArray(structureArrayFieldName, arrayElement)); + } + default: + { + std::string msg = "unsupported type: " + type; + throw EpicsException(msg); + } + } +} + +StructureConstPtr IntrospectionRegistry::deserializeStructureField(ByteBuffer* buffer, DeserializableControl* control, IntrospectionRegistry* registry) +{ + checkBufferAndDeserializeControl(buffer, control); + const String structureFieldName = SerializeHelper::deserializeString(buffer, control); + const int32 size = SerializeHelper::readSize(buffer, control); + FieldConstPtrArray fields = NULL; + if (size > 0) + { + fields = new FieldConstPtr[size]; + for(int i = 0; i < size; i++) + { + fields[i] = deserialize(buffer, control, registry); + } + } + //TODO stucture constructor created new fields instead of taking this ones + return _fieldCreate->createStructure(structureFieldName, size, fields); +} + +void IntrospectionRegistry::serializeStructure(ByteBuffer* buffer, SerializableControl* control, PVStructurePtr pvStructure) +{ + checkBufferAndSerializeControl(buffer, control); + if (pvStructure == NULL) + { + serialize(NULL, buffer, control); + } + else + { + serialize(pvStructure->getField(), buffer, control); + pvStructure->serialize(buffer, control); + } +} + +PVStructurePtr IntrospectionRegistry::deserializeStructure(ByteBuffer* buffer, DeserializableControl* control) +{ + checkBufferAndDeserializeControl(buffer, control); + PVStructurePtr pvStructure = NULL; + FieldConstPtr structureField = deserialize(buffer, control); + if (structureField != NULL) + { + pvStructure = _pvDataCreate->createPVStructure(NULL, static_cast(structureField)); + pvStructure->deserialize(buffer, control); + } + return pvStructure; +} + +void IntrospectionRegistry::serializePVRequest(ByteBuffer* buffer, SerializableControl* control, PVStructurePtr pvRequest) +{ + checkBufferAndSerializeControl(buffer, control); + // for now ordinary structure, later can be changed + serializeStructure(buffer, control, pvRequest); +} + +PVStructurePtr IntrospectionRegistry::deserializePVRequest(ByteBuffer* buffer, DeserializableControl* control) +{ + checkBufferAndDeserializeControl(buffer, control); + // for now ordinary structure, later can be changed + return deserializeStructure(buffer, control); +} + +PVStructurePtr IntrospectionRegistry::deserializeStructureAndCreatePVStructure(ByteBuffer* buffer, DeserializableControl* control) +{ + checkBufferAndDeserializeControl(buffer, control); + FieldConstPtr field = deserialize(buffer, control); + if (field == NULL) + { + return NULL; + } + return _pvDataCreate->createPVStructure(NULL,static_cast(field)); +} + +void IntrospectionRegistry::serializeStatus(ByteBuffer* buffer, SerializableControl* control, Status* status) +{ + checkBufferAndSerializeControl(buffer, control); + if(status != NULL) + { + status->serialize(buffer, control); + } + else + { + throw EpicsException("null status provided"); + } +} + +Status* IntrospectionRegistry::deserializeStatus(ByteBuffer* buffer, DeserializableControl* control) +{ + checkBufferAndDeserializeControl(buffer, control); + return _statusCreate->deserializeStatus(buffer, control); +} + +void IntrospectionRegistry::checkBufferAndSerializeControl(ByteBuffer* buffer, SerializableControl* control) +{ + if(buffer == NULL) + { + throw EpicsException("null buffer provided"); + } + if(control == NULL) + { + throw EpicsException("null control provided"); + } +} + +void IntrospectionRegistry::checkBufferAndDeserializeControl(ByteBuffer* buffer, DeserializableControl* control) +{ + if(buffer == NULL) + { + throw EpicsException("null buffer provided"); + } + if(control == NULL) + { + throw EpicsException("null control provided"); + } +} + +}} + diff --git a/pvAccessApp/utils/introspectionRegistry.h b/pvAccessApp/utils/introspectionRegistry.h new file mode 100644 index 0000000..dd32d1d --- /dev/null +++ b/pvAccessApp/utils/introspectionRegistry.h @@ -0,0 +1,242 @@ +/* + * introspectionRegistry.h + */ + +#ifndef INTROSPECTIONREGISTRY_H +#define INTROSPECTIONREGISTRY_H + +#include +#include +#include + +#include "lock.h" +#include "pvIntrospect.h" +#include "pvData.h" +#include "byteBuffer.h" +#include "serialize.h" +#include "serializeHelper.h" +#include "status.h" +#include "standardField.h" + +using namespace epics::pvData; +using namespace std; + +namespace epics { namespace pvAccess { + +typedef std::map registryMap_t; + + + /** + * PVData Structure registry. + * Registry is used to cache introspection interfaces to minimize network traffic. + * @author gjansa + */ + class IntrospectionRegistry { + public: + IntrospectionRegistry(bool serverSide); + virtual ~IntrospectionRegistry(); + void printKeysAndValues(string name); + /** + * Resets registry, i.e. must be done when transport is changed (server restarted). + */ + void reset(); + /** + * Get introspection interface for given ID. + * + * @param id id of the introspection interface to get + * + * @return Field instance for given ID. + */ + FieldConstPtr getIntrospectionInterface(const short id); + + /** + * Registers introspection interface with given ID. Always INCOMING. + * + * @param id id of the introspection interface to register + * @param field introspection interface to register + */ + void registerIntrospectionInterface(const short id, FieldConstPtr field); + + /** + * Registers introspection interface and get it's ID. Always OUTGOING. + * If it is already registered only preassigned ID is returned. + * @param field introspection interface to register + * + * @return id of given introspection interface + */ + short registerIntrospectionInterface(FieldConstPtr field, bool& existing); + + /** + * Serializes introspection interface + * + * @param field + * @param buffer + * @param control + */ + void serialize(FieldConstPtr field, ByteBuffer* buffer, SerializableControl* control); + + /** + * Deserializes introspection interface + * + * TODO + * + * @param buffer + * @param control + * + * @return Field deserialized from the buffer. + */ + FieldConstPtr deserialize(ByteBuffer* buffer, DeserializableControl* control); + + /** + * Serializes introspection interface. But this time really fully not like + * the serialize which only says it serializes but in fact does not. :) + * + * TODO + * + * @param field + * @param buffer + * @param control + */ + static void serializeFull(FieldConstPtr field, ByteBuffer* buffer, SerializableControl* control); + + /** + * Deserializes introspection interface + * + * TODO + * + * @param buffer + * @param control + * + * @return Field deserialized from the buffer. + */ + static FieldConstPtr deserializeFull(ByteBuffer* buffer, DeserializableControl* control); + + /** + * Null type. + */ + const static int8 NULL_TYPE_CODE; + + /** + * Serialization contains only an ID (that was assigned by one of the previous FULL_WITH_ID descriptions). + */ + const static int8 ONLY_ID_TYPE_CODE; + + /** + * Serialization contains an ID (that can be used later, if cached) and full interface description. + */ + const static int8 FULL_WITH_ID_TYPE_CODE; + + + static void serialize(FieldConstPtr field, StructureConstPtr parent, ByteBuffer* buffer, + SerializableControl* control, IntrospectionRegistry* registry); + + + /** + * @param buffer + * @param control + * @param registry + * @param structure + */ + static void serializeStructureField(ByteBuffer* buffer, SerializableControl* control, + IntrospectionRegistry* registry, StructureConstPtr structure); + + /** + * @param buffer + * @param control + * @param registry + * @param structure + */ + static FieldConstPtr deserialize(ByteBuffer* buffer, DeserializableControl* control, IntrospectionRegistry* registry); + + /** + * Deserialize Structure. + * @param buffer + * @param control + * @param registry + * @return deserialized Structure instance. + */ + static StructureConstPtr deserializeStructureField(ByteBuffer* buffer, DeserializableControl* control, IntrospectionRegistry* registry); + + /** + * Serialize optional PVStructrue. + * @param buffer data buffer. + */ + void serializeStructure(ByteBuffer* buffer, SerializableControl* control, PVStructurePtr pvStructure); + + /** + * Deserialize optional PVStructrue. + * @param payloadBuffer data buffer. + * @return deserialized PVStructure, can be null. + */ + PVStructurePtr deserializeStructure(ByteBuffer* payloadBuffer, DeserializableControl* control); + + /** + * Serialize PVRequest. + * @param buffer data buffer. + */ + void serializePVRequest(ByteBuffer* buffer, SerializableControl* control, PVStructurePtr pvRequest); + + /** + * Deserialize PVRequest. + * @param payloadBuffer data buffer. + * @param control + * + * @return deserialized PVRequest, can be null. + */ + PVStructurePtr deserializePVRequest(ByteBuffer* payloadBuffer, DeserializableControl* control); + + /** + * Deserialize Structure and create PVStructure instance. + * @param payloadBuffer data buffer. + * @return PVStructure instance, can be null. + */ + PVStructurePtr deserializeStructureAndCreatePVStructure(ByteBuffer* payloadBuffer, DeserializableControl* control); + + /** + * Serialize status. + * TODO optimize duplicates + * @param buffer data buffer. + * @param control serializaiton control instance. + * @param status status to serialize. + */ + void serializeStatus(ByteBuffer* buffer, SerializableControl* control, Status* status); + + /** + * Serialize status. + * TODO optimize duplicates + * @param buffer data buffer. + */ + Status* deserializeStatus(ByteBuffer* buffer, DeserializableControl* control); + + private: + registryMap_t _registry; + registryMap_t::iterator _registryIter; + registryMap_t::reverse_iterator _registryRIter; + short _outgoingIdPointer; + short _direction; + Mutex _mutex; + + /** + * PVField factory. + */ + static PVDataCreate* _pvDataCreate; + + /** + * Status factory. + */ + static StatusCreate* _statusCreate; + + /** + * Field factory. + */ + static FieldCreate* _fieldCreate; + + bool registryContainsValue(FieldConstPtr field, short& key); + bool compareFields(FieldConstPtr field1, FieldConstPtr field2); + static void checkBufferAndSerializeControl(ByteBuffer* buffer, SerializableControl* control); + static void checkBufferAndDeserializeControl(ByteBuffer* buffer, DeserializableControl* control); + }; + +}} + +#endif /* INTROSPECTIONREGISTRY_H */ diff --git a/testApp/client/Makefile b/testApp/client/Makefile index 12d5f58..2eee695 100644 --- a/testApp/client/Makefile +++ b/testApp/client/Makefile @@ -6,6 +6,10 @@ PROD_HOST += testChannelAccessFactory testChannelAccessFactory_SRCS += testChannelAccessFactory.cpp testChannelAccessFactory_LIBS += pvData pvAccess Com +PROD_HOST += testCreateRequest +testCreateRequest_SRCS += testCreateRequest.cpp +testCreateRequest_LIBS += pvData pvAccess Com + PROD_HOST += testMockClient testMockClient_SRCS = testMockClient.cpp MockClientImpl.cpp testMockClient_LIBS = pvData pvAccess Com diff --git a/testApp/client/MockClientImpl.cpp b/testApp/client/MockClientImpl.cpp index 4a1c75a..40dc7b7 100644 --- a/testApp/client/MockClientImpl.cpp +++ b/testApp/client/MockClientImpl.cpp @@ -8,250 +8,9 @@ #include #include -#include - using namespace epics::pvData; using namespace epics::pvAccess; -using std::string; - -class CreateRequestFactory { - private: - - static void trim(std::string& str) - { - std::string::size_type pos = str.find_last_not_of(' '); - if(pos != std::string::npos) { - str.erase(pos + 1); - pos = str.find_first_not_of(' '); - if(pos != std::string::npos) str.erase(0, pos); - } - else str.erase(str.begin(), str.end()); - } - - static int findMatchingBrace(string& request, int index, int numOpen) { - size_t openBrace = request.find('{', index+1); - size_t closeBrace = request.find('}', index+1); - if(openBrace == std::string::npos && closeBrace == std::string::npos) return std::string::npos; - if (openBrace != std::string::npos) { - if(openBraceopenBrace)) { - //find matching brace - size_t closeBrace = findMatchingBrace(request,openBrace+1,1); - if(closeBrace==std::string::npos) { - requester->message(request + "mismatched { }", errorMessage); - return false; - } - String fieldName = request.substr(0,openBrace); - PVStructure* pvStructure = getPVDataCreate()->createPVStructure(pvParent, fieldName, 0); - createFieldRequest(pvStructure,request.substr(openBrace+1,closeBrace-openBrace-1),false,requester); - pvParent->appendPVField(pvStructure); - if(request.length()>closeBrace+1) { - if(request.at(closeBrace+1) != ',') { - requester->message(request + "misssing , after }", errorMessage); - return false; - } - if(!createFieldRequest(pvParent,request.substr(closeBrace+2),false,requester)) return false; - } - return true; - } - if(openBracket==std::string::npos && fieldListOK) { - PVString* pvStringField = static_cast(getPVDataCreate()->createPVScalar(pvParent, "fieldList", pvString)); - pvStringField->put(request); - pvParent->appendPVField(pvStringField); - return true; - } - if(openBracket!=std::string::npos && (comma==std::string::npos || comma>openBracket)) { - size_t closeBracket = request.find(']'); - if(closeBracket==std::string::npos) { - requester->message(request + "option does not have matching []", errorMessage); - return false; - } - if(!createLeafFieldRequest(pvParent,request.substr(0, closeBracket+1),requester)) return false; - if(request.rfind(',')>closeBracket) { - int nextComma = request.find(',', closeBracket); - if(!createFieldRequest(pvParent,request.substr(nextComma+1),false,requester)) return false; - } - return true; - } - if(comma!=std::string::npos) { - if(!createLeafFieldRequest(pvParent,request.substr(0, comma),requester)) return false; - return createFieldRequest(pvParent,request.substr(comma+1),false,requester); - } - return createLeafFieldRequest(pvParent,request,requester); - } - - static bool createLeafFieldRequest(PVStructure* pvParent,String request,Requester* requester) { - size_t openBracket = request.find('['); - String fullName = request; - if(openBracket != std::string::npos) fullName = request.substr(0,openBracket); - size_t indLast = fullName.rfind('.'); - String fieldName = fullName; - if(indLast>1 && indLast != std::string::npos) fieldName = fullName.substr(indLast+1); - PVStructure* pvStructure = getPVDataCreate()->createPVStructure(pvParent, fieldName, 0); - PVStructure* pvLeaf = getPVDataCreate()->createPVStructure(pvStructure,"leaf", 0); - PVString* pvStringField = static_cast(getPVDataCreate()->createPVScalar(pvLeaf, "source", pvString)); - pvStringField->put(fullName); - pvLeaf->appendPVField(pvStringField); - if(openBracket != std::string::npos) { - size_t closeBracket = request.find(']'); - if(closeBracket==std::string::npos) { - requester->message("option does not have matching []", errorMessage); - return false; - } - if(!createRequestOptions(pvLeaf,request.substr(openBracket+1, closeBracket-openBracket-1),requester)) return false; - } - pvStructure->appendPVField(pvLeaf); - pvParent->appendPVField(pvStructure); - return true; - } - - static bool createRequestOptions(PVStructure* pvParent,std::string request,Requester* requester) { - trim(request); - if(request.length()<=1) return true; - - std::string token; - std::istringstream iss(request); - while (getline(iss, token, ',')) - { - size_t equalsPos = token.find('='); - size_t equalsRPos = token.rfind('='); - if (equalsPos != equalsRPos) - { - requester->message("illegal option ", errorMessage); - return false; - } - - PVString* pvStringField = static_cast(getPVDataCreate()->createPVScalar(pvParent, token.substr(0, equalsPos), pvString)); - pvStringField->put(token.substr(equalsPos+1)); - pvParent->appendPVField(pvStringField); - } - return true; - } - - public: - - /** - * Create a request structure for the create calls in Channel. - * See the package overview documentation for details. - * @param request The field request. See the package overview documentation for details. - * @param requester The requester; - * @return The request structure if an invalid request was given. - */ - static PVStructure* createRequest(String request, Requester* requester) - { - static String emptyString; - if (!request.empty()) trim(request); - if (request.empty()) - { - return getPVDataCreate()->createPVStructure(0, emptyString, 0); - } - - size_t offsetRecord = request.find("record["); - size_t offsetField = request.find("field("); - size_t offsetPutField = request.find("putField("); - size_t offsetGetField = request.find("getField("); - - PVStructure* pvStructure = getPVDataCreate()->createPVStructure(0, emptyString, 0); - if (offsetRecord != std::string::npos) { - size_t offsetBegin = request.find('[', offsetRecord); - size_t offsetEnd = request.find(']', offsetBegin); - if(offsetEnd == std::string::npos) { - delete pvStructure; - requester->message("record[ does not have matching ]", errorMessage); - return 0; - } - PVStructure* pvStruct = getPVDataCreate()->createPVStructure(pvStructure, "record", 0); - if(!createRequestOptions(pvStruct,request.substr(offsetBegin+1, offsetEnd-offsetBegin-1),requester)) - { - // TODO is this ok? - delete pvStruct; - delete pvStructure; - return 0; - } - pvStructure->appendPVField(pvStruct); - } - if (offsetField != std::string::npos) { - size_t offsetBegin = request.find('(', offsetField); - size_t offsetEnd = request.find(')', offsetBegin); - if(offsetEnd == std::string::npos) { - delete pvStructure; - requester->message("field( does not have matching )", errorMessage); - return 0; - } - PVStructure* pvStruct = getPVDataCreate()->createPVStructure(pvStructure, "field", 0); - if(!createFieldRequest(pvStruct,request.substr(offsetBegin+1, offsetEnd-offsetBegin-1),true,requester)) - { - delete pvStruct; - delete pvStructure; - return 0; - } - pvStructure->appendPVField(pvStruct); - } - if (offsetPutField != std::string::npos) { - size_t offsetBegin = request.find('(', offsetPutField); - size_t offsetEnd = request.find(')', offsetBegin); - if(offsetEnd == std::string::npos) { - delete pvStructure; - requester->message("putField( does not have matching )", errorMessage); - return 0; - } - PVStructure* pvStruct = getPVDataCreate()->createPVStructure(pvStructure, "putField", 0); - if(!createFieldRequest(pvStruct,request.substr(offsetBegin+1, offsetEnd-offsetBegin-1),true,requester)) - { - delete pvStruct; - delete pvStructure; - return 0; - } - pvStructure->appendPVField(pvStruct); - } - if (offsetGetField != std::string::npos) { - size_t offsetBegin = request.find('(', offsetGetField); - size_t offsetEnd = request.find(')', offsetBegin); - if(offsetEnd == std::string::npos) { - delete pvStructure; - requester->message("getField( does not have matching )", errorMessage); - return 0; - } - PVStructure* pvStruct = getPVDataCreate()->createPVStructure(pvStructure, "getField", 0); - if(!createFieldRequest(pvStruct,request.substr(offsetBegin+1, offsetEnd-offsetBegin-1),true,requester)) - { - delete pvStruct; - delete pvStructure; - return 0; - } - pvStructure->appendPVField(pvStruct); - } - if (pvStructure->getStructure()->getNumberFields()==0) { - if(!createFieldRequest(pvStructure,request,true,requester)) - { - delete pvStructure; - return 0; - } - } - return pvStructure; - } - -}; - - - - - static volatile int64 mockChannelProcess_totalConstruct = 0; static volatile int64 mockChannelProcess_totalDestruct = 0; static Mutex *mockChannelProcess_globalMutex = 0; @@ -1302,7 +1061,6 @@ class ChannelProcessRequesterImpl : public ChannelProcessRequester }; -/* int main(int argc,char *argv[]) { MockClientContext* context = new MockClientContext(); @@ -1363,116 +1121,3 @@ int main(int argc,char *argv[]) getShowConstructDestruct()->constuctDestructTotals(stdout); return(0); } - -*/ - - -class RequesterImpl : public Requester { - public: - - virtual String getRequesterName() - { - return "RequesterImpl"; - }; - - virtual void message(String message,MessageType messageType) - { - std::cout << "[" << getRequesterName() << "] message(" << message << ", " << messageTypeName[messageType] << ")" << std::endl; - } -}; - -#include - -static void testCreateRequest() { - RequesterImpl requester; - String out; - String request = ""; - PVStructure* pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest); - out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; - delete pvRequest; - - request = "alarm,timeStamp,power.value"; - pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest); - out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; - delete pvRequest; - - request = "record[process=true]field(alarm,timeStamp,power.value)"; - pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest); - out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; - delete pvRequest; - - request = "record[process=true]field(alarm,timeStamp[algorithm=onChange,causeMonitor=false],power{power.value,power.alarm})"; - pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest); - out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; - delete pvRequest; - - request = "record[process=true,xxx=yyy]field(alarm,timeStamp[shareData=true],power.value)"; - pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest); - out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; - delete pvRequest; - - request = String("record[process=true,xxx=yyy]") - + "putField(power.value)" - + "getField(alarm,timeStamp,power{power.value,power.alarm}," - + "current{current.value,current.alarm},voltage{voltage.value,voltage.alarm})"; - pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest); - out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; - delete pvRequest; - - request = String("record[process=true,xxx=yyy]") - + "putField(power.value)" - + "getField(alarm,timeStamp,power{power.value,power.alarm}," - + "current{current.value,current.alarm},voltage{voltage.value,voltage.alarm}," - + "ps0{" - + "ps0.alarm,ps0.timeStamp,power{ps0.power.value,ps0.power.alarm}," - + "current{ps0.current.value,ps0.current.alarm},voltage{ps0.voltage.value,ps0.voltage.alarm}}," - + "ps1{" - + "ps1.alarm,ps1.timeStamp,power{ps1.power.value,ps1.power.alarm}," - + "current{ps1.current.value,ps1.current.alarm},voltage{ps1.voltage.value,ps1.voltage.alarm}" - + "})"; - pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest); - out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; - delete pvRequest; - - request = "a{b{c{d}}}"; - pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest); - out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; - delete pvRequest; - - request = "record[process=true,xxx=yyy]field(alarm,timeStamp[shareData=true],power.value"; - std::cout << std::endl << "Error Expected for next call!!" << std::endl; - pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest==0); - - request = String("record[process=true,xxx=yyy]") - + "putField(power.value)" - + "getField(alarm,timeStamp,power{power.value,power.alarm}," - + "current{current.value,current.alarm},voltage{voltage.value,voltage.alarm}," - + "ps0{" - + "ps0.alarm,ps0.timeStamp,power{ps0.power.value,ps0.power.alarm}," - + "current{ps0.current.value,ps0.current.alarm},voltage{ps0.voltage.value,ps0.voltage.alarm}}," - + "ps1{" - + "ps1.alarm,ps1.timeStamp,power{ps1.power.value,ps1.power.alarm}," - + "current{ps1.current.value,ps1.current.alarm},voltage{ps1.voltage.value,ps1.voltage.alarm}" - + ")"; - std::cout << std::endl << "Error Expected for next call!!" << std::endl; - pvRequest = CreateRequestFactory::createRequest(request,&requester); - assert(pvRequest==0); -} - -int main(int argc,char *argv[]) -{ - testCreateRequest(); - - std::cout << "-----------------------------------------------------------------------" << std::endl; - getShowConstructDestruct()->constuctDestructTotals(stdout); - return 0; -} diff --git a/testApp/client/testCreateRequest.cpp b/testApp/client/testCreateRequest.cpp new file mode 100644 index 0000000..e1d7a17 --- /dev/null +++ b/testApp/client/testCreateRequest.cpp @@ -0,0 +1,158 @@ +/* testCreateRequest.cpp */ +/* Author: Matej Sekoranja Date: 2010.12.27 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace epics::pvData; +using namespace epics::pvAccess; + + +class RequesterImpl : public Requester { + public: + + virtual String getRequesterName() + { + return "RequesterImpl"; + }; + + virtual void message(String message,MessageType messageType) + { + std::cout << "[" << getRequesterName() << "] message(" << message << ", " << messageTypeName[messageType] << ")" << std::endl; + } +}; + + +void testCreateRequest() { + printf("testCreateRequest... "); + + RequesterImpl requester; + String out; + String request = ""; + PVStructure* pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest); + out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; + delete pvRequest; + + request = "alarm,timeStamp,power.value"; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest); + out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; + delete pvRequest; + + request = "record[process=true]field(alarm,timeStamp,power.value)"; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest); + out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; + delete pvRequest; + + request = "record[process=true]field(alarm,timeStamp[algorithm=onChange,causeMonitor=false],power{power.value,power.alarm})"; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest); + out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; + delete pvRequest; + + request = "record[process=true,xxx=yyy]field(alarm,timeStamp[shareData=true],power.value)"; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest); + out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; + delete pvRequest; + + request = String("record[process=true,xxx=yyy]") + + "putField(power.value)" + + "getField(alarm,timeStamp,power{power.value,power.alarm}," + + "current{current.value,current.alarm},voltage{voltage.value,voltage.alarm})"; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest); + out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; + delete pvRequest; + + request = String("record[process=true,xxx=yyy]") + + "putField(power.value)" + + "getField(alarm,timeStamp,power{power.value,power.alarm}," + + "current{current.value,current.alarm},voltage{voltage.value,voltage.alarm}," + + "ps0{" + + "ps0.alarm,ps0.timeStamp,power{ps0.power.value,ps0.power.alarm}," + + "current{ps0.current.value,ps0.current.alarm},voltage{ps0.voltage.value,ps0.voltage.alarm}}," + + "ps1{" + + "ps1.alarm,ps1.timeStamp,power{ps1.power.value,ps1.power.alarm}," + + "current{ps1.current.value,ps1.current.alarm},voltage{ps1.voltage.value,ps1.voltage.alarm}" + + "})"; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest); + out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; + delete pvRequest; + + request = "a{b{c{d}}}"; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest); + out.clear(); pvRequest->toString(&out); std::cout << out << std::endl; + delete pvRequest; + + request = "record[process=true,xxx=yyy]field(alarm,timeStamp[shareData=true],power.value"; + std::cout << std::endl << "Error Expected for next call!!" << std::endl; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest==0); + + request = String("record[process=true,xxx=yyy]") + + "putField(power.value)" + + "getField(alarm,timeStamp,power{power.value,power.alarm}," + + "current{current.value,current.alarm},voltage{voltage.value,voltage.alarm}," + + "ps0{" + + "ps0.alarm,ps0.timeStamp,power{ps0.power.value,ps0.power.alarm}," + + "current{ps0.current.value,ps0.current.alarm},voltage{ps0.voltage.value,ps0.voltage.alarm}}," + + "ps1{" + + "ps1.alarm,ps1.timeStamp,power{ps1.power.value,ps1.power.alarm}," + + "current{ps1.current.value,ps1.current.alarm},voltage{ps1.voltage.value,ps1.voltage.alarm}" + + ")"; + std::cout << std::endl << "Error Expected for next call!!" << std::endl; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest==0); + + request = "record[process=true,power.value"; + std::cout << std::endl << "Error Expected for next call!!" << std::endl; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest==0); + + request = "field(power.value"; + std::cout << std::endl << "Error Expected for next call!!" << std::endl; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest==0); + + request = "putField(power.value"; + std::cout << std::endl << "Error Expected for next call!!" << std::endl; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest==0); + + request = "getField(power.value"; + std::cout << std::endl << "Error Expected for next call!!" << std::endl; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest==0); + + request = "record[process=true=power.value]"; + std::cout << std::endl << "Error Expected for next call!!" << std::endl; + pvRequest = getCreateRequest()->createRequest(request,&requester); + assert(pvRequest==0); + + printf("PASSED\n"); + +} + +int main(int argc,char *argv[]) +{ + testCreateRequest(); + + std::cout << "-----------------------------------------------------------------------" << std::endl; + getShowConstructDestruct()->constuctDestructTotals(stdout); + return 0; +} + + diff --git a/testApp/utils/Makefile b/testApp/utils/Makefile index e83fc7d..49343d0 100644 --- a/testApp/utils/Makefile +++ b/testApp/utils/Makefile @@ -26,6 +26,10 @@ PROD_HOST += loggerTest loggerTest_SRCS += loggerTest.cpp loggerTest_LIBS += pvAccess pvData Com +PROD_HOST += introspectionRegisterTest +introspectionRegisterTest_SRCS += introspectionRegistryTest.cpp +introspectionRegisterTest_LIBS += pvAccess Com pvData + include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE diff --git a/testApp/utils/introspectionRegistryTest.cpp b/testApp/utils/introspectionRegistryTest.cpp new file mode 100644 index 0000000..7851ad0 --- /dev/null +++ b/testApp/utils/introspectionRegistryTest.cpp @@ -0,0 +1,467 @@ +/* + * introspectionRegistryTest.cpp + * + * Created on: Dec 23, 2010 + * Author: Gasper Jansa + */ + +#include "introspectionRegistry.h" + +#include +#include +#include +#include +#include + +namespace epics { + namespace pvAccess { + + class SerializableControlImpl : public SerializableControl, + public NoDefaultMethods { + public: + virtual void flushSerializeBuffer() { + } + + virtual void ensureBuffer(int size) { + } + + SerializableControlImpl() { + } + + virtual ~SerializableControlImpl() { + } + }; + + class DeserializableControlImpl : public DeserializableControl, + public NoDefaultMethods { + public: + virtual void ensureData(int size) { + } + + DeserializableControlImpl() { + } + + virtual ~DeserializableControlImpl() { + } + }; + + } +} + +using namespace epics::pvAccess; +using namespace std; + +static SerializableControl* flusher; +static DeserializableControl* control; +static ByteBuffer* buffer; + +static PVDataCreate* pvDataCreate; +static StatusCreate* statusCreate; +static FieldCreate* fieldCreate; +static StandardField *standardField; + +static IntrospectionRegistry* registry; +static IntrospectionRegistry* clientRegistry; +static IntrospectionRegistry* serverRegistry; + +vector pvFieldArray; + +static string builder; + +//helper methods +ScalarConstPtr getScalar(string name) +{ + ScalarConstPtr scalar = standardField->scalar(name,pvFloat); + PVField *pvField = pvDataCreate->createPVField(0,scalar); + pvFieldArray.push_back(pvField); + return scalar; +} + +ScalarArrayConstPtr getScalarArray(string name) +{ + ScalarArrayConstPtr scalarArray = standardField->scalarArray(name,pvFloat); + PVField *pvField = pvDataCreate->createPVField(0,scalarArray); + pvFieldArray.push_back(pvField); + return scalarArray; +} + +StructureConstPtr getStructure(string name) +{ + String properties("alarm"); + FieldConstPtr powerSupply[3]; + powerSupply[0] = standardField->scalar( + String("voltage"),pvDouble,properties); + PVField *pvField = pvDataCreate->createPVField(0,powerSupply[0]); + pvFieldArray.push_back(pvField); + powerSupply[1] = standardField->scalar( + String("power"),pvDouble,properties); + pvField = pvDataCreate->createPVField(0,powerSupply[1]); + pvFieldArray.push_back(pvField); + powerSupply[2] = standardField->scalar( + String("current"),pvDouble,properties); + pvField = pvDataCreate->createPVField(0,powerSupply[2]); + pvFieldArray.push_back(pvField); + StructureConstPtr structure = standardField->structure(name,3,powerSupply); + pvField = pvDataCreate->createPVField(0,structure); + pvFieldArray.push_back(pvField); + return structure; +} + +StructureArrayConstPtr getStructureArray(string name1, string name2) +{ + String properties("alarm"); + FieldConstPtr powerSupply[3]; + powerSupply[0] = standardField->scalar( + String("voltage"),pvDouble,properties); + PVField *pvField = pvDataCreate->createPVField(0,powerSupply[0]); + pvFieldArray.push_back(pvField); + powerSupply[1] = standardField->scalar( + String("power"),pvDouble,properties); + pvField = pvDataCreate->createPVField(0,powerSupply[1]); + pvFieldArray.push_back(pvField); + powerSupply[2] = standardField->scalar( + String("current"),pvDouble,properties); + pvField = pvDataCreate->createPVField(0,powerSupply[2]); + pvFieldArray.push_back(pvField); + StructureConstPtr structure = standardField->structure(name1,3,powerSupply); + StructureArrayConstPtr structureArray = standardField->structureArray(name2,structure); + pvField = pvDataCreate->createPVField(0,structureArray); + pvFieldArray.push_back(pvField); + return structureArray; +} + +//test methods +void testRegistryPutGet() +{ + short id = 0; + ScalarConstPtr scalarIn = getScalar("field1"); + registry->registerIntrospectionInterface(id,scalarIn); + + id++; + ScalarArrayConstPtr scalarArrayIn = getScalarArray("fieldArray1"); + registry->registerIntrospectionInterface(id,scalarArrayIn); + + id++; + StructureConstPtr structureIn = getStructure("struct1"); + registry->registerIntrospectionInterface(id,structureIn); + + id++; + StructureArrayConstPtr structureArrayIn = getStructureArray("struct1","structArray1"); + registry->registerIntrospectionInterface(id,structureArrayIn); + + id = 0; + ScalarConstPtr scalarOut = static_cast(registry->getIntrospectionInterface(id)); + assert(scalarIn == scalarOut); + + id++; + ScalarArrayConstPtr scalarArrayOut = static_cast(registry->getIntrospectionInterface(id)); + assert(scalarArrayIn == scalarArrayOut); + + id++; + StructureConstPtr structureOut = static_cast(registry->getIntrospectionInterface(id)); + assert(structureIn == structureOut); + + id++; + StructureArrayConstPtr structureArrayOut = static_cast(registry->getIntrospectionInterface(id)); + assert(structureArrayIn == structureArrayOut); + + bool existing; + id = registry->registerIntrospectionInterface(static_cast(scalarIn),existing); + assert(existing == true); + assert(id == 0); + + id = registry->registerIntrospectionInterface(static_cast(scalarArrayIn),existing); + assert(existing == true); + assert(id == 1); + + id = registry->registerIntrospectionInterface(static_cast(structureIn),existing); + assert(existing == true); + assert(id == 2); + + id = registry->registerIntrospectionInterface(static_cast(structureArrayIn),existing); + assert(existing == true); + assert(id == 3); + + //should exist + ScalarConstPtr scalarInNew = getScalar("field1"); + id = registry->registerIntrospectionInterface(static_cast(scalarInNew),existing); + assert(existing == true); + assert(id == 0); + + scalarOut = static_cast(registry->getIntrospectionInterface(id)); + assert(scalarIn == scalarOut); +} + +void testRegistryReset() +{ + registry->reset(); + + short id = 0; + try //there is no elements so exception will be thrown + { + static_cast(registry->getIntrospectionInterface(id)); + } + catch(EpicsException& e) + { + return; + } + assert(false); +} + +void serialize(FieldConstPtr field, IntrospectionRegistry* registry) +{ + buffer->clear(); + registry->serialize(field,buffer,flusher); + //should be in registry + bool existing; + registry->registerIntrospectionInterface(field,existing); + assert(existing == true); +} + +FieldConstPtr deserialize(IntrospectionRegistry* registry) +{ + FieldConstPtr field = registry->deserialize(buffer,control); + PVField *pvField = pvDataCreate->createPVField(0,field); + pvFieldArray.push_back(pvField); + //should be in registry + bool existing; + registry->registerIntrospectionInterface(field,existing); + assert(existing == true); + return field; +} + +void checkTypeCode(int8 typeCodeIn) +{ + int8 typeCode = buffer->getByte(); + assert(typeCode == typeCodeIn); + buffer->rewind(); +} + +void testSerializeCommon(FieldConstPtr serverField1, FieldConstPtr clientField2) +{ + //server serializes field 1 + serialize(serverField1,serverRegistry); + + //full should be serialized + buffer->flip(); + checkTypeCode(IntrospectionRegistry::FULL_WITH_ID_TYPE_CODE); + + //client deserializes 1 + FieldConstPtr clientField1 = deserialize(clientRegistry); + assert(serverField1->getFieldName() == clientField1->getFieldName()); + assert(serverField1->getType() == clientField1->getType()); + + //client serializes the same field + serialize(serverField1,clientRegistry); + + //only id should be serialized + buffer->flip(); + checkTypeCode(IntrospectionRegistry::ONLY_ID_TYPE_CODE); + + //server deserializes the same field + serverField1 = deserialize(serverRegistry); + assert(serverField1->getFieldName() == clientField1->getFieldName()); + assert(serverField1->getType() == clientField1->getType()); + + //client requests new field + serialize(clientField2,clientRegistry); + + //full should be serialized + buffer->flip(); + checkTypeCode(IntrospectionRegistry::FULL_WITH_ID_TYPE_CODE); + + //server deserializes new field + FieldConstPtr serverField2 = deserialize(serverRegistry); + assert(serverField2->getFieldName() == clientField2->getFieldName()); + assert(serverField2->getType() == clientField2->getType()); +} + +void testSerialize() +{ + clientRegistry->reset(); + serverRegistry->reset(); + stringstream ss; + string name1,name2,name3,name4; + + for(int i = 0, j = 0; i < 1 ; i++, j++) + { + name1.clear(); + name2.clear(); + ss.str(""); + ss << j; + name1 = "field" + ss.str(); + ss.str(""); + j++; + ss << j; + name2 = "field" + ss.str(); + testSerializeCommon(static_cast(getScalar(name1)),static_cast(getScalar(name2))); + + name1.clear(); + name2.clear(); + ss.str(""); + ss << j; + name1 = "fieldArray" + ss.str(); + ss.str(""); + j++; + ss << j; + name2 = "fieldArray" + ss.str(); + testSerializeCommon(static_cast(getScalarArray(name1)),static_cast(getScalarArray(name2))); + + name1.clear(); + name2.clear(); + ss.str(""); + ss << j; + name1 = "structure" + ss.str(); + ss.str(""); + j++; + ss << j; + name2 = "structure" + ss.str(); + testSerializeCommon(static_cast(getStructure(name1)),static_cast(getStructure(name2))); + + name1.clear(); + name2.clear(); + name3.clear(); + name4.clear(); + ss.str(""); + ss << j; + name1 = "structure" + ss.str(); + name2 = "structureArray" + ss.str(); + ss.str(""); + j++; + ss << j; + name3 = "structure" + ss.str(); + name4 = "structureArray" + ss.str(); + testSerializeCommon(static_cast(getStructureArray(name1,name2)),static_cast(getStructureArray(name3,name4))); + } + + serverRegistry->printKeysAndValues("server"); + //clientRegistry->printKeysAndValues("client"); +} + +void testSerializeFull() +{ + buffer->clear(); + ScalarConstPtr scalarIn = getScalar("field1"); + IntrospectionRegistry::serializeFull(static_cast(scalarIn),buffer,flusher); + buffer->flip(); + ScalarConstPtr scalarOut = static_cast(IntrospectionRegistry::deserializeFull(buffer,control)); + PVField *pvField = pvDataCreate->createPVField(0,scalarOut); + pvFieldArray.push_back(pvField); + assert(scalarIn->getFieldName() == scalarOut->getFieldName()); + assert(scalarIn->getType() == scalarOut->getType()); + + buffer->clear(); + ScalarArrayConstPtr scalarArrayIn = getScalarArray("fieldArray1"); + IntrospectionRegistry::serializeFull(static_cast(scalarArrayIn),buffer,flusher); + buffer->flip(); + ScalarArrayConstPtr scalarArrayOut = static_cast(IntrospectionRegistry::deserializeFull(buffer,control)); + pvField = pvDataCreate->createPVField(0,scalarArrayOut); + pvFieldArray.push_back(pvField); + assert(scalarArrayIn->getFieldName() == scalarArrayOut->getFieldName()); + assert(scalarArrayIn->getType() == scalarArrayOut->getType()); + + buffer->clear(); + StructureConstPtr structureIn = getStructure("struct1"); + IntrospectionRegistry::serializeFull(static_cast(structureIn),buffer,flusher); + buffer->flip(); + StructureConstPtr structureOut = static_cast(IntrospectionRegistry::deserializeFull(buffer,control)); + pvField = pvDataCreate->createPVField(0,structureOut); + pvFieldArray.push_back(pvField); + assert(structureIn->getFieldName() == structureOut->getFieldName()); + assert(structureIn->getType() == structureOut->getType()); + + buffer->clear(); + StructureArrayConstPtr structureArrayIn = getStructureArray("struct1","structArray1"); + IntrospectionRegistry::serializeFull(static_cast(structureArrayIn),buffer,flusher); + buffer->flip(); + StructureArrayConstPtr structureArrayOut = static_cast(IntrospectionRegistry::deserializeFull(buffer,control)); + pvField = pvDataCreate->createPVField(0,structureArrayOut); + pvFieldArray.push_back(pvField); + assert(structureArrayIn->getFieldName() == structureArrayOut->getFieldName()); + assert(structureArrayIn->getType() == structureArrayOut->getType()); +} + +void testSerializePVRequest() +{ + buffer->clear(); + registry->reset(); + PVStructurePtr pvStructureIn = pvDataCreate->createPVStructure(NULL,getStructure("structure1")); + registry->serializePVRequest(buffer,flusher,pvStructureIn); + + buffer->flip(); + PVStructurePtr pvStructureOut = registry->deserializePVRequest(buffer,control); + assert(*pvStructureIn == *pvStructureOut); + delete pvStructureIn; + delete pvStructureOut; +} + +void testDeserializeStructureAndCreatePVStructure() +{ + buffer->clear(); + registry->reset(); + StructureConstPtr structureIn = getStructure("structure1"); + serialize(structureIn,registry); + + buffer->flip(); + PVStructurePtr pvStructureOut = registry->deserializeStructureAndCreatePVStructure(buffer,control); + StructureConstPtr structureOut = pvStructureOut->getStructure(); + assert(structureIn->getFieldName() == structureOut->getFieldName()); + assert(structureIn->getType() == structureOut->getType()); + delete pvStructureOut; +} + +void testSerializeStatus() +{ + buffer->clear(); + registry->reset(); + Status* statusIn = statusCreate->getStatusOK(); + registry->serializeStatus(buffer,flusher,statusIn); + + buffer->flip(); + Status* statusOut= registry->deserializeStatus(buffer,control); + assert(statusIn->getType() == statusOut->getType()); + delete statusOut; + //TODO why are in and out on the same address? +} + +int main(int argc, char *argv[]) { + //TODO something is wrong with freeing structure and structure array, should be checked. + + pvDataCreate = getPVDataCreate(); + statusCreate = getStatusCreate(); + fieldCreate = getFieldCreate(); + standardField = getStandardField(); + + flusher = new SerializableControlImpl(); + control = new DeserializableControlImpl(); + buffer = new ByteBuffer(1<<16); + registry = new IntrospectionRegistry(true); + clientRegistry = new IntrospectionRegistry(false); + serverRegistry = new IntrospectionRegistry(true); + + + testRegistryPutGet(); + testRegistryReset(); + testSerialize(); + testSerializeFull(); + testDeserializeStructureAndCreatePVStructure(); + testSerializeStatus(); + + registry->reset(); + clientRegistry->reset(); + serverRegistry->reset(); + for (unsigned int i=0; i < pvFieldArray.size(); i++) + { + delete pvFieldArray[i]; + } + pvFieldArray.clear(); + + if(flusher) delete flusher; + if(control) delete control; + if(buffer) delete buffer; + if(registry) delete registry; + if(clientRegistry) delete clientRegistry; + if(serverRegistry) delete serverRegistry; + cout << "DONE" << endl; + return 0; +}