?? udldcfg.c
字號:
} /* end - else (not first entry in table) */
} /* UdprotAddSymbol */
/***********************************************************************/
/** prepare WRITE message for indicated item and new value
The client has sent data to be written to a point in the logical device.
The message header and the new data are formatted and placed in the write
buffer. The message will be sent the next time UdprotPoll() is called by
ProtTimerEvent().
returns TRUE if successful **/
BOOL
WINAPI
UdprotPrepareWriteMsg (LPSTAT lpTopic,
SYMPTR lpSymEnt,
PTVALUE ptValue)
{
SYMPTR lpSymOld;
LPSTR lpData;
LPUDMSG lpMsg, lpFirstMsg;
LPUDMSG lpFoundMsg, lpAppendMsg;
unsigned long SymHandle;
INTG tmp, given_value;
INTG RegMin, RegMax;
PTQUALITY ptQuality;
WORD w[2];
volatile REAL *rp;
volatile WORD *wp;
volatile INTG *ip;
BOOL bIsStr = FALSE;
int iStrLen, iOutLen;
LPSTR lpszInStr;
BYTE pOutData[UD_MAX_WRITE_LENGTH];
BOOL ok, discrete, can_extend, changed;
BOOL folding, found, new_symbol;
WORD count, count_limit, numBytes, msgSize, allocSize;
WORD myAddr, endAddr;
WORD length, myOff;
BYTE plcDataType, subType;
BYTE msgType;
BYTE DDEType;
int numFound, found_index;
BYTE FAR *pByte;
WORD blockSize, extSize, message_limit;
CHAINSCANNER message_scanner;
BYTE plcAddr;
#ifdef DEBUG_CALL_TRAFFIC
if (Verbose)
debug("UdprotPrepareWriteMsg( %04lX, %04lX, %04lX )",
(long) lpTopic, (long) lpSymEnt, (long) ptValue.intg);
#endif
/* initialize return value */
ok = TRUE;
/* initialize default quality */
ptQuality = WW_SQ_GOOD;
/* get handle for symbol table entry */
assert(lpSymEnt);
SymHandle = lpSymEnt->msIndex + SYM_OFFSET;
/* set type of data message, data from symbol table entry */
plcDataType = (BYTE) lpSymEnt->msPlcDataType;
DDEType = (BYTE) lpSymEnt->msDdeType;
subType = (BYTE) lpSymEnt->msSubType;
myAddr = lpSymEnt->msAddr1;
count = lpSymEnt->msCount;
endAddr = (WORD) (myAddr + count - 1);
numBytes = (WORD) lpSymEnt->msNumBytes;
discrete = FALSE;
msgType = (BYTE) (plcDataType | 0x80); /* device-specific */
/* check whether write is possible */
if (subType == PLCS_BITP) {
/* cannot write bits in integer memory */
return (FALSE);
}
/** ensure that data is correct for appropriate plcDataType, subType
Note: this depends on device, protocol defined point types, etc. **/
switch (subType) {
case PLCS_NONE:
case PLCS_WORD:
case PLCS_SIGNED:
case PLCS_BCD:
/* determine valid range */
switch (subType) {
case PLCS_SIGNED:
RegMin = -32768;
RegMax = 32767;
break;
case PLCS_BCD:
RegMin = 0;
RegMax = 9999;
break;
default:
RegMin = 0;
RegMax = 65535;
break;
} /* switch */
/* force new values to valid range */
tmp = ptValue.intg;
given_value = tmp;
if (tmp < RegMin) {
tmp = RegMin;
ptQuality = WW_SQ_CLAMPLO;
}
if (tmp > RegMax) {
tmp = RegMax;
ptQuality = WW_SQ_CLAMPHI;
}
if (tmp != given_value) {
if (ShowingErrors) {
/* have logger show error */
debug ("%s value %ld out of range for PlcDataType=%d, "
"Addr=0x%04lX, clamped to %ld.",
((subType==PLCS_BCD) ? "BCD write" : "Write"),
(long) given_value, (int) plcDataType,
(long) myAddr, (long) tmp);
}
}
if (subType == PLCS_BCD) {
/* convert clamped binary value to BCD */
ptValue.intg = (INTG) UdprotCvtBinToBCD ((WORD) tmp);
} else {
/* use clamped binary value */
ptValue.intg = tmp;
}
if (ptQuality != WW_SQ_GOOD) {
/* indicate data has been clamped at high or low end */
DbNewVQFromDevice (lpTopic->statIdLogDev, lpSymEnt->msDbHnd,
ptValue, ptQuality);
}
break;
case PLCS_DWORD:
/* swap words so they get output in the right order */
ip = (INTG *) w;
wp = (WORD *) &ptValue;
w[1] = *(wp++);
w[0] = *wp;
// 3/10/2000 modify by Chen jun
{
long dValue = w[0] * 65536 + w[1];
w[0] = dValue%10000;
w[1] = dValue/10000;
}
// modify end
ptValue.intg = *ip;
break;
case PLCS_REAL:
/* swap words so they get output in the right order */
rp = (REAL *) w;
wp = (WORD *) &ptValue;
// 3/10/2000 modify by Chen jun
//w[1] = *(wp++);
//w[0] = *wp;
w[0] = *(wp++);
w[1] = *wp;
// modify end
ptValue.real = *rp;
break;
case PLCS_STRC:
case PLCS_STRP:
case PLCS_STRB:
case PLCS_STR_:
/* indicate item is a string, get pointer to string buffer */
bIsStr = TRUE;
lpszInStr = StrValStringLock (ptValue);
if (lpszInStr == (LPSTR) NULL)
{
return (FALSE);
}
/* ensure string is no longer than what we can transmit in one message */
count_limit = LimitStringLength (myAddr, count, numBytes, FALSE);
if (count_limit < count)
{
/* too long, truncate string to manageable length */
count = count_limit;
endAddr = (WORD) (myAddr + count - 1);
}
/* set up string data according to type */
iStrLen = min (numBytes * count,
(WORD) (UD_MAX_WRITE_LENGTH - 1));
/* check length of resulting string */
if (iStrLen < (int) _fstrlen (lpszInStr)) {
/* indicate string is truncated */
ptQuality = WW_SQ_CLAMPHI;
_fstrncpy ((LPSTR) pOutData, lpszInStr, (size_t) iStrLen);
pOutData[iStrLen] = '\0';
ptValue = StrValInitialString ((LPSTR) &pOutData[0]);
DbNewVQFromDevice (lpTopic->statIdLogDev, lpSymEnt->msDbHnd,
ptValue, ptQuality);
}
switch (subType)
{
case PLCS_STRC:
_fstrncpy ((LPSTR) pOutData, lpszInStr, (size_t) iStrLen);
pOutData[iStrLen - 1] = '\0';
break;
case PLCS_STRP:
_fstrncpy ((LPSTR) &pOutData[1], lpszInStr,
(size_t) iStrLen - 1);
pOutData[iStrLen] = '\0';
pOutData[0] = (BYTE) strlen ((char *) &pOutData[1]);
break;
case PLCS_STRB:
case PLCS_STR_:
_fstrncpy ((LPSTR) pOutData, lpszInStr, (size_t) iStrLen);
iOutLen = strlen ((char *) pOutData);
if (iOutLen < iStrLen)
{
memset (&pOutData[iOutLen], ' ',
(size_t) (iStrLen - iOutLen));
}
break;
} /* switch */
/* Now swap the bytes so that when the message is built as words
the chars will be in the right order */
swab ((char *) pOutData, (char *) pOutData, (iStrLen + 1) & ~1);
StrValStringUnlock (ptValue);
break;
default:
/* no checking of data */
break;
} /* switch */
/* copy ptValue to pOutData if data type was other than string */
if (!bIsStr)
{
memcpy (pOutData, &ptValue, (size_t) sizeof (PTVALUE));
}
/*********************************************************************\
There are two main ways write traffic may be reduced:
1. Folded writes.
When multiple writes to one particular data point have
accumulated, send only the most recent data.
Since the client may be toggling the point between two
values, be sure that at least two values are sent.
This is accomplished by replacing the data for an existing
message with a more recent value for the point and then
regenerating the message.
2. Optimized writes.
Instead of generating a new write message for each point
extend a compatible existing message, if any. This is
accomplished by extending the data for an existing message
with data for a new point and then regenerating the message.
Note: Optimized writes may or may not be possible, depending on
the communication protocol for the device. How to make
the extension depends on such things as addressing modes
and the partitioning of device memory. Also, it is
important to make sure that the extended message is not
too long for the device protocol.
Take care when locking and unlocking symbol table entries,
especially if the protocol supports writes to multiple
non-contiguous memory locations. For example, if the message
is only writing to points 1, 3, and 5 you should lock those
points, but NOT points 2 and 4. The same goes for unlocking
when the message is deleted.
The example code below is for a simulated device that permits
reading and writing up to 22 consecutive memory locations of the
same data type. So a point is included if it is of the same
plcDataType and falls within the address range mmStartAddr..mmEndAddr.
And the message can be extended if it is of the same plcDataType and
the point is just before or after the range mmStartAddr..mmEndAddr.
\*********************************************************************/
/* get lengths needed to write and extend a message for this point */
GetBldMsgWriteBlockSizes (&blockSize, &extSize,
msgType, plcDataType, myAddr, count);
/* get max length for message */
message_limit = UD_MAX_MESSAGE_SIZE;
/* get pointer to first message in write list */
lpFirstMsg = (LPUDMSG) FindFirstItem (&lpTopic->statWriteMsgList,
SCAN_FROM_HEAD,
NULL, NULL, &message_scanner);
/* initialize search variables */
found = FALSE;
lpFoundMsg = (LPUDMSG) NULL;
lpAppendMsg = (LPUDMSG) NULL;
numFound = 0;
folding = FALSE;
found_index = 0;
/* try to find an existing message into which the write can be placed */
lpMsg = lpFirstMsg;
while ((lpMsg != (LPUDMSG) NULL) && (!found)) {
/*********************************************************\
We never modify the first message in the queue to
prevent hanging up all of the write messages because
of one point that is being written too frequently.
\*********************************************************/
/* check whether message includes the symbol of interest */
if ((lpMsg->mmMsgType == msgType) &&
(lpMsg->mmStartAddr <= myAddr) && (endAddr <= lpMsg->mmEndAddr)) {
/* message includes this symbol, count number of matches found */
/** There must be at least the minimum number of writes
pending, so a point won't get stuck at one value
when the client is trying to alternate values **/
numFound++;
/* check whether we can fold into this message */
if ((lpMsg != lpFirstMsg) &&
(numFound >= FOLDMIN)) {
/* not first message, and we have found enough matches */
/* indicate message found for folded write */
found = TRUE;
lpFoundMsg = lpMsg;
folding = TRUE;
found_index = myAddr - lpMsg->mmStartAddr;
}
}
/* check for optimized write only if we haven't already found one */
if ((!found) && (lpAppendMsg == (LPUDMSG) NULL) &&
(lpMsg != lpFirstMsg)) {
/* check whether we can append to this message */
can_extend = (lpMsg->mmMsgType == msgType) &&
((lpMsg->mmEndAddr+1 == myAddr) ||
(endAddr+1 == lpMsg->mmStartAddr));
if (can_extend && (lpMsg->mmSize + extSize <= message_limit)) {
/* save pointer to message for later */
lpAppendMsg = lpMsg;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -