def update_recurse(con, submodel, path):
            err = -1
            if len(path) < 1:
                logger.error("recusion into model went wrong")
                err = -1
            elif len(path) == 1:
                if submodel[path[0]] and 'reftype' in submodel[
                        path[0]] and submodel[path[0]]['reftype'] == 'DA':
                    fcName = submodel[path[0]]['FC']
                    #read DA
                    fc = lib61850.FunctionalConstraint_fromString(fcName)
                    error = lib61850.IedClientError()
                    value = lib61850.IedConnection_readObject(
                        con, ctypes.byref(error), ref, fc)
                    if error.value == 0:
                        submodel[path[0]]['value'], submodel[
                            path[0]]['type'] = iec61850client.printValue(value)
                        lib61850.MmsValue_delete(value)
                        err = 0
                    else:
                        logger.error("could not read DA: %s from device" % ref)
                        err = -1

                else:
                    submodel[path[0]] = iec61850client.printDataDirectory(
                        con, ref)
                    if submodel[path[0]]:  # check if value or empty returned
                        err = 0
                    else:
                        err = -1
            else:
                submodel[path[0]], err = update_recurse(
                    con, submodel[path[0]], path[1:])
            return submodel, err
	def getIED(self, host, port):
		if port == "" or port == None:
			port = 102

		if host == None:
			logger.error("missing hostname")
			return -1

		tupl = host + ":" + str(port)
		if tupl in self.connections and self.connections[tupl]["con"] != None:
			if not self.connections[tupl]["model"]:
				con = self.connections[tupl]["con"]
				model = iec61850client.discovery(con)
				if model: #if model is not empty
					# store the model
					self.connections[tupl]["model"] = model
					return 0
				else:
					#we could not perform a discovery, so remove connection
					lib61850.IedConnection_destroy(con)
					self.connections[tupl]["con"] = None
					return -1
			else:
				#we have a connection and a model
				return 0
		
		if not tupl in self.connections:
			self.connections[tupl] = {}
			self.connections[tupl]["con"] = None
			self.connections[tupl]["model"] = {}

		con = lib61850.IedConnection_create()
		error = lib61850.IedClientError()
		lib61850.IedConnection_connect(con,ctypes.byref(error), host, port)
		if error.value == lib61850.IED_ERROR_OK:
			# store the active connection
			self.connections[tupl]["con"] = con
			# read the model
			model = iec61850client.discovery(con)
			if model: #if model is not empty
				# store the model
				self.connections[tupl]["model"] = model
				return 0
			else:
				return -1
		else:
			lib61850.IedConnection_destroy(con)
			return -1
    def printDataDirectory(con, doRef):
        tmodel = {}
        if doRef.find("/") == -1:
            logger.error("invalid datadirecory")
            return {}

        error = lib61850.IedClientError()
        dataAttributes = lib61850.IedConnection_getDataDirectoryFC(
            con, ctypes.byref(error), doRef)

        if error.value != 0:
            logger.error("could not get logical device list, error:%i" %
                         error.value)

        if dataAttributes:
            dataAttribute = lib61850.LinkedList_getNext(dataAttributes)

            while dataAttribute:
                daName = ctypes.cast(
                    lib61850.LinkedList_getData(dataAttribute),
                    ctypes.c_char_p).value.decode("utf-8")
                daRef = doRef + "." + daName[:-4]
                fcName = daName[-3:-1]

                submodel = iec61850client.printDataDirectory(con, daRef)
                if submodel:
                    tmodel[daName[:-4]] = submodel

                else:
                    tmodel[daName[:-4]] = {}
                    tmodel[daName[:-4]]['reftype'] = "DA"
                    tmodel[daName[:-4]]['FC'] = fcName
                    tmodel[daName[:-4]]['value'] = "UNKNOWN"
                    #read DA
                    fc = lib61850.FunctionalConstraint_fromString(fcName)
                    value = lib61850.IedConnection_readObject(
                        con, ctypes.byref(error), daRef, fc)

                    if error.value == 0:
                        tmodel[daName[:-4]]['value'], tmodel[daName[:-4]][
                            'type'] = iec61850client.printValue(value)
                        lib61850.MmsValue_delete(value)

                dataAttribute = lib61850.LinkedList_getNext(dataAttribute)

            lib61850.LinkedList_destroy(dataAttributes)
        return tmodel
	def writeValue(con, model, ref, value):
		submodel, path = iec61850client.parseRef(model,ref)

		if not submodel:
			logger.error("cannot find ref: %s in model" % ref)
			return {},-1
		if not 'FC' in submodel:
			logger.error("ref is not DA")
			return {},-1

		fc = lib61850.FunctionalConstraint_fromString(submodel['FC']) 
		mmsvalue = iec61850client.getMMsValue(submodel['type'],value)
		if not mmsvalue:
			return model,-1

		error = lib61850.IedClientError()
		lib61850.IedConnection_writeObject(con, ctypes.byref(error), ref, fc, mmsvalue)
		lib61850.MmsValue_delete(mmsvalue)
		if error.value == 0:
			model, err = iec61850client.updateValueInModel(con, model, ref)
			return model, err
		return model, error.value
	def discovery(con):
		tmodel = {}

		error = lib61850.IedClientError()
		deviceList = lib61850.IedConnection_getLogicalDeviceList(con, ctypes.byref(error))

		if error.value != 0:
			logger.error("could not get logical device list, error:%i" % error)

		if deviceList:
			device = lib61850.LinkedList_getNext(deviceList)
			while device:
				LD_name=ctypes.cast(lib61850.LinkedList_getData(device),ctypes.c_char_p).value.decode("utf-8")
				tmodel[LD_name] = {}

				logicalNodes = lib61850.IedConnection_getLogicalDeviceDirectory(con, ctypes.byref(error), LD_name)
				if error.value != 0:#ret becomes int if connection is lost
					lib61850.LinkedList_destroy(deviceList)
					return model
					
				logicalNode = lib61850.LinkedList_getNext(logicalNodes)
				while logicalNode:
					LN_name=ctypes.cast(lib61850.LinkedList_getData(logicalNode),ctypes.c_char_p).value.decode("utf-8")
					tmodel[LD_name][LN_name] = {}

					#[LNobjects, error] = iec61850.IedConnection_getLogicalNodeVariables(con, LD_name+"/"+LN_name)
					LNobjects = lib61850.IedConnection_getLogicalNodeDirectory(con, ctypes.byref(error), LD_name+"/"+LN_name,lib61850.ACSI_CLASS_DATA_OBJECT)
					if error.value != 0:#ret becomes int if connection is lost
						lib61850.LinkedList_destroy(logicalNodes)
						lib61850.LinkedList_destroy(deviceList)
						return model

					LNobject = lib61850.LinkedList_getNext(LNobjects)
					while LNobject:
						Do = ctypes.cast(lib61850.LinkedList_getData(LNobject),ctypes.c_char_p).value.decode("utf-8")
						tmodel[LD_name][LN_name][Do] = {}

						doRef = LD_name+"/"+LN_name+"."+Do

						tmodel[LD_name][LN_name][Do] = iec61850client.printDataDirectory(con, doRef)

						LNobject = lib61850.LinkedList_getNext(LNobject)
					lib61850.LinkedList_destroy(LNobjects)

					LNdss = lib61850.IedConnection_getLogicalNodeDirectory(con, ctypes.byref(error), LD_name+"/"+LN_name, lib61850.ACSI_CLASS_DATA_SET)
					if error.value != 0:#ret becomes int if connection is lost
						lib61850.LinkedList_destroy(logicalNodes)
						lib61850.LinkedList_destroy(deviceList)
						return tmodel

					LNds = lib61850.LinkedList_getNext(LNdss)
					while LNds:

						DSname = ctypes.cast(lib61850.LinkedList_getData(LNds),ctypes.c_char_p).value.decode("utf-8")
						tmodel[LD_name][LN_name][DSname] = {}

						#cannot pass the right type to isDeletable(last arg).. keeps complaining about 'bool *', and isDel = ctypes.pointer(ctypes.c_bool(False)) does not work
						isDel = ctypes.c_bool(False)
						dataSetMembers = lib61850.IedConnection_getDataSetDirectory(con, ctypes.byref(error), LD_name+"/"+LN_name+"."+DSname, ctypes.byref(isDel))  
						if error.value != 0:#ret becomes int if connection is lost
							lib61850.LinkedList_destroy(LNdss)
							lib61850.LinkedList_destroy(logicalNodes)
							lib61850.LinkedList_destroy(deviceList)
							return tmodel

						#all DS are assumed not deletable 
						if isDel == True:
							logger.error("  DS: %s, not Deletable" % DSname)
						else:
							logger.error("  DS: %s, is Deletable" % DSname)
						dataSetMemberRef = lib61850.LinkedList_getNext(dataSetMembers)

						i = 0
						while dataSetMemberRef:
							dsRef = ctypes.cast(lib61850.LinkedList_getData(dataSetMemberRef),ctypes.c_char_p).value.decode("utf-8")
							DX = dsRef[:-4]
							FC = dsRef[-3:-1]
							tmodel[LD_name][LN_name][DSname][str(i)] = {}
							tmodel[LD_name][LN_name][DSname][str(i)]['reftype'] = "DX"
							tmodel[LD_name][LN_name][DSname][str(i)]['type'] = "reference"
							tmodel[LD_name][LN_name][DSname][str(i)]['value'] = DX
							tmodel[LD_name][LN_name][DSname][str(i)]['FC'] = FC
							dataSetMemberRef = lib61850.LinkedList_getNext(dataSetMemberRef)
							i += 1
						lib61850.LinkedList_destroy(dataSetMembers)
						LNds = lib61850.LinkedList_getNext(LNds)

					lib61850.LinkedList_destroy(LNdss)
					logicalNode = lib61850.LinkedList_getNext(logicalNode)

				lib61850.LinkedList_destroy(logicalNodes)
				device = lib61850.LinkedList_getNext(device)

			lib61850.LinkedList_destroy(deviceList)
		return tmodel