Merging release 2.0 branch with CVS trunk
r2601 | ffr | 2008-05-30 10:26:57 +1000 (Fri, 30 May 2008) | 2 lines
This commit is contained in:
committed by
Douglas Clowes
parent
4a937e1608
commit
0749b0effa
@@ -680,7 +680,7 @@
|
||||
*/ struct AsynSrv__info *asyn_info) {
|
||||
|
||||
int status;
|
||||
char cmnd[8], rply[8];
|
||||
//char cmnd[8], rply[8];
|
||||
/*----------------------------------------------
|
||||
** Pre-set the routine name (in case of error)
|
||||
*/
|
||||
@@ -898,8 +898,8 @@
|
||||
/* ==================
|
||||
*/ char *par_id,
|
||||
...) {
|
||||
int i;
|
||||
char buff[4];
|
||||
//int i;
|
||||
//char buff[4];
|
||||
va_list ap; /* Pointer to variable args */
|
||||
char *txt_ptr;
|
||||
int intval;
|
||||
@@ -971,7 +971,7 @@
|
||||
int *my_errno,
|
||||
int *vaxc_errno) {
|
||||
|
||||
int i, j, k;
|
||||
int i;//, j, k;
|
||||
char buff[80];
|
||||
|
||||
if (AsynSrv_call_depth <= 0) {
|
||||
@@ -1034,7 +1034,7 @@
|
||||
*/ struct AsynSrv__info *asyn_info) {
|
||||
|
||||
int status;
|
||||
char cmnd[8], rply[8];
|
||||
//char cmnd[8], rply[8];
|
||||
/*----------------------------------------------
|
||||
** Pre-set the routine name (in case of error)
|
||||
*/
|
||||
@@ -1124,25 +1124,25 @@
|
||||
*/ struct AsynSrv__info *asyn_info) {
|
||||
|
||||
int i, status;
|
||||
int my_skt;
|
||||
char old_time_out[4];
|
||||
union {
|
||||
char chars[4];
|
||||
int val;
|
||||
} time_out;
|
||||
//int my_skt;
|
||||
//char old_time_out[4];
|
||||
//union {
|
||||
// char chars[4];
|
||||
// int val;
|
||||
//} time_out;
|
||||
char buff[128];
|
||||
struct RS__MsgStruct s_buff;
|
||||
struct RS__RespStruct r_buff;
|
||||
unsigned int oto_len, oto_status;
|
||||
struct hostent *rmt_hostent;
|
||||
struct in_addr *rmt_inet_addr_pntr;
|
||||
struct in_addr rmt_inet_addr;
|
||||
int rmt_sockname_len;
|
||||
struct sockaddr_in lcl_sockname;
|
||||
struct sockaddr_in rmt_sockname;
|
||||
//struct RS__MsgStruct s_buff;
|
||||
//struct RS__RespStruct r_buff;
|
||||
//unsigned int oto_len, oto_status;
|
||||
//struct hostent *rmt_hostent;
|
||||
//struct in_addr *rmt_inet_addr_pntr;
|
||||
//struct in_addr rmt_inet_addr;
|
||||
//int rmt_sockname_len;
|
||||
//struct sockaddr_in lcl_sockname;
|
||||
//struct sockaddr_in rmt_sockname;
|
||||
|
||||
char *errtxt_ptr;
|
||||
int errcode, my_errno, my_vaxc_errno;
|
||||
//char *errtxt_ptr;
|
||||
//int errcode, my_errno, my_vaxc_errno;
|
||||
/*--------------------------------------------------------
|
||||
*/
|
||||
asyn_info->skt = 0;
|
||||
@@ -1225,17 +1225,17 @@
|
||||
/* ===============
|
||||
*/ struct AsynSrv__info *asyn_info) {
|
||||
|
||||
int i, status;
|
||||
int status; //,i;
|
||||
int my_skt;
|
||||
char old_time_out[4];
|
||||
union {
|
||||
char chars[4];
|
||||
int val;
|
||||
} time_out;
|
||||
//char old_time_out[4];
|
||||
//union {
|
||||
// char chars[4];
|
||||
// int val;
|
||||
//} time_out;
|
||||
char buff[128];
|
||||
struct RS__MsgStruct s_buff;
|
||||
struct RS__RespStruct r_buff;
|
||||
unsigned int oto_len, oto_status;
|
||||
//unsigned int oto_len, oto_status;
|
||||
struct hostent *rmt_hostent;
|
||||
struct in_addr *rmt_inet_addr_pntr;
|
||||
struct in_addr rmt_inet_addr;
|
||||
@@ -1243,8 +1243,8 @@
|
||||
struct sockaddr_in lcl_sockname;
|
||||
struct sockaddr_in rmt_sockname;
|
||||
|
||||
char *errtxt_ptr;
|
||||
int errcode, my_errno, my_vaxc_errno;
|
||||
//char *errtxt_ptr;
|
||||
//int errcode, my_errno, my_vaxc_errno;
|
||||
/*--------------------------------------------------------
|
||||
*/
|
||||
asyn_info->skt = 0;
|
||||
@@ -1478,7 +1478,7 @@
|
||||
int i, status, c_len, size, max_size, ncmnds;
|
||||
int bytes_to_come, bytes_left;
|
||||
char *nxt_byte_ptr;
|
||||
char err_text[80];
|
||||
// char err_text[80];
|
||||
char text[20];
|
||||
va_list ap; /* Pointer to variable args */
|
||||
char *txt_ptr;
|
||||
@@ -1724,7 +1724,7 @@
|
||||
int i, status, size, max_size, ncmnds;
|
||||
int bytes_to_come, bytes_left;
|
||||
char *nxt_byte_ptr;
|
||||
char err_text[80];
|
||||
//char err_text[80];
|
||||
char text[20];
|
||||
va_list ap; /* Pointer to variable args */
|
||||
int *c_len, s_len;
|
||||
@@ -2046,7 +2046,7 @@
|
||||
int state) {
|
||||
|
||||
int status;
|
||||
char cmnd[8], rply[8];
|
||||
char cmnd[8];//, rply[8];
|
||||
/*----------------------------------------------
|
||||
** Pre-set the routine name (in case of error)
|
||||
*/
|
||||
@@ -2091,7 +2091,7 @@
|
||||
*/ struct AsynSrv__info *asyn_info) {
|
||||
|
||||
int status;
|
||||
char cmnd[8], rply[8];
|
||||
//char cmnd[8], rply[8];
|
||||
/*----------------------------------------------
|
||||
** Pre-set the routine name (in case of error)
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@ SRC = .
|
||||
CC = gcc
|
||||
CFLAGS = -g -DLINUX $(DFORTIFY) -I$(SRC) -I../.. -Wall -Wno-unused
|
||||
|
||||
HOBJ= itc4util.o lh45util.o lakeshore340util.o asynsrv_utility.o geterrno.o strjoin.o chopper.o
|
||||
HOBJ= nhq200util.o itc4util.o lh45util.o lakeshore340util.o west4100util.o asynsrv_utility.o geterrno.o strjoin.o chopper.o modbustcp.o
|
||||
|
||||
libhlib.a: $(HOBJ)
|
||||
rm -f libhlib.a
|
||||
|
||||
135
site_ansto/hardsup/modbustcp.c
Normal file
135
site_ansto/hardsup/modbustcp.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*---------------------------------------------------------------------------
|
||||
M O D B U S T C P . C
|
||||
|
||||
Paul Barron, January 2008
|
||||
|
||||
RTU Modbus functions designed for use with the WEST4100 Temperature Controller.
|
||||
If another modbus device is required at ANSTO sections of this code will need
|
||||
to be modified.
|
||||
|
||||
MBAP: Modbus Application Protocol Header
|
||||
PDU: Protocol Data Unit
|
||||
|
||||
Modbus TCP Packet Format
|
||||
| MBAP | PDU |
|
||||
|Transact Identifier|Protocol Identifier|Length Field|Unit ID|Funct Code|Data|
|
||||
| 2 Bytes | 2 Bytes | 2 Bytes | 1 Byte| 1 Byte | n |
|
||||
|
||||
Paul Barron, January 2008
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <rs232controller.h>
|
||||
#include "modbustcp.h"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
int ModbusTCPException(unsigned char *response);
|
||||
|
||||
int MsgGenModbusTCP(unsigned char *ModbusMsg, int ModbusMsgLength, unsigned char *TcpPacket,
|
||||
int *TcpPacketLength)
|
||||
{
|
||||
unsigned char MBAPbyte1 = 1, MBAPbyte2 = 1;
|
||||
unsigned char lengthByte1, lengthByte2;
|
||||
int index;
|
||||
|
||||
// Check if Device Address is Valid. This is based on RS485 having up to
|
||||
// 32 devices, since we currently only have one device this won't be a problem.
|
||||
if ((ModbusMsg[0] > 32) || (ModbusMsg[0] < 1))
|
||||
{
|
||||
printf("Error: Modbus Address out of Range: %X\n",ModbusMsg[0]);
|
||||
return MODBUSTCP_BadDataAddress;
|
||||
}
|
||||
|
||||
// Check if Function code is Valid
|
||||
if ((ModbusMsg[1] > 16) || (ModbusMsg[1] < 1))
|
||||
{
|
||||
printf("Error: Modbus Function Code Invalid: %X\n",ModbusMsg[1]);
|
||||
return MODBUSTCP_BadFunction;
|
||||
}
|
||||
|
||||
// Calculate Legth Field
|
||||
// Length should never be greater than 255 but just in case.
|
||||
if ((ModbusMsgLength)>255){
|
||||
lengthByte1=ModbusMsgLength/255;
|
||||
lengthByte2=ModbusMsgLength%255;
|
||||
}
|
||||
else{
|
||||
lengthByte1=0;
|
||||
lengthByte2=ModbusMsgLength;
|
||||
}
|
||||
|
||||
sprintf((char *)TcpPacket,"%c%c%c%c%c%c",MBAPbyte1,MBAPbyte2,0,0,lengthByte1,lengthByte2);
|
||||
|
||||
for(index=0;index<=ModbusMsgLength;index++)sprintf((char *)&TcpPacket[index+6],"%c",ModbusMsg[index]);
|
||||
|
||||
*TcpPacketLength=ModbusMsgLength+6;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
int transactModbusTCP(prs232 self, unsigned char *query, int queryLength, unsigned char *response, int responseLength)
|
||||
{
|
||||
unsigned char TCPquery[40];
|
||||
int iRet, index, TCPqueryLength;
|
||||
|
||||
// Generate the TCP message
|
||||
iRet=MsgGenModbusTCP(query,queryLength,TCPquery,&TCPqueryLength);
|
||||
|
||||
// Send the message and Read the reply
|
||||
memset(response,0,responseLength); // puts zeros in reply up until reply length
|
||||
if ((iRet=transactRS232(self,TCPquery,/*strlen(pCommand)*/12,response,20))<=0)
|
||||
{
|
||||
printf("Comms error!\n");
|
||||
return iRet; // Comms problem
|
||||
}
|
||||
|
||||
// Check that the response transact and protocol identifier are the same
|
||||
if( (strncmp((char *)TCPquery,(char *)response,3)) == 0)
|
||||
{
|
||||
// Check that there is not a modbus error, see page 98 from WEST4100 User manual.
|
||||
if ( response[7] > 0x80 )
|
||||
{
|
||||
iRet=ModbusTCPException(response);
|
||||
|
||||
return iRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return the modbus response minus the TCP Header
|
||||
for(index=0;index<6;index++)
|
||||
response[index]=response[index+6];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1; // Success
|
||||
}
|
||||
|
||||
return MODBUSTCP_TCPError;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
int ModbusTCPException(unsigned char *response)
|
||||
{
|
||||
if(response[8] == 0x01){
|
||||
printf("Exception Code 01h: Illegal Function\n");
|
||||
return MODBUSTCP_IllegalFunction;
|
||||
}else if(response[8] == 0x02){
|
||||
printf("Exception Code 02h: Illegal Data Address\n");
|
||||
return MODBUSTCP_IllegalDataAddress;
|
||||
}else if(response[8] == 0x03){
|
||||
printf("Exception Code 03h: Illegal Data Value\n");
|
||||
return MODBUSTCP_IllegalDataValue;
|
||||
}else{
|
||||
printf("Error code is greater than 81h, 82h or 83h\n");
|
||||
return MODBUSTCP_UnsupportedError;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
28
site_ansto/hardsup/modbustcp.h
Normal file
28
site_ansto/hardsup/modbustcp.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------
|
||||
M O D B U S T C P . H
|
||||
|
||||
Modbus functions designed for use with the WEST4100 Temperature Controller.
|
||||
|
||||
Paul Barron, 2008
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef MODBUSTCP
|
||||
#define MODBUSTCP
|
||||
#include "rs232controller.h"
|
||||
|
||||
// Own Codes
|
||||
#define MODBUSTCP_BadFunction -8001
|
||||
#define MODBUSTCP_BadDataAddress -8002
|
||||
#define MODBUSTCP_IllegalFunction -8011
|
||||
#define MODBUSTCP_IllegalDataAddress -8012
|
||||
#define MODBUSTCP_IllegalDataValue -8013
|
||||
#define MODBUSTCP_UnsupportedError -8014 // Device returned a modbus error that is not 81, 82 or 83
|
||||
#define MODBUSTCP_TCPError -8016 // Repsonse Transaction and Protocol Identifier do no match query
|
||||
#define MODBUSTCP_UnknownError -8017
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
int transactModbusTCP(prs232 self, unsigned char *query, int queryLength, unsigned char *response, int responseLength);
|
||||
|
||||
#endif
|
||||
@@ -105,14 +105,14 @@
|
||||
{
|
||||
int status;
|
||||
struct SerialInfo *my_info;
|
||||
void *my_hndl;
|
||||
struct hostent *rmt_hostent;
|
||||
struct in_addr *rmt_inet_addr_pntr;
|
||||
int rmt_sockname_len;
|
||||
struct sockaddr_in lcl_sockname;
|
||||
struct sockaddr_in rmt_sockname;
|
||||
char msr_cmnd[20];
|
||||
struct RS__RplyStruct *rply_ptr;
|
||||
//void *my_hndl;
|
||||
//struct hostent *rmt_hostent;
|
||||
//struct in_addr *rmt_inet_addr_pntr;
|
||||
//int rmt_sockname_len;
|
||||
//struct sockaddr_in lcl_sockname;
|
||||
//struct sockaddr_in rmt_sockname;
|
||||
//char msr_cmnd[20];
|
||||
//struct RS__RplyStruct *rply_ptr;
|
||||
|
||||
*pData = NULL;
|
||||
|
||||
@@ -158,14 +158,14 @@
|
||||
{
|
||||
int status;
|
||||
struct SerialInfo *my_info;
|
||||
void *my_hndl;
|
||||
struct hostent *rmt_hostent;
|
||||
struct in_addr *rmt_inet_addr_pntr;
|
||||
int rmt_sockname_len;
|
||||
struct sockaddr_in lcl_sockname;
|
||||
struct sockaddr_in rmt_sockname;
|
||||
char msr_cmnd[20];
|
||||
struct RS__RplyStruct *rply_ptr;
|
||||
//void *my_hndl;
|
||||
//struct hostent *rmt_hostent;
|
||||
//struct in_addr *rmt_inet_addr_pntr;
|
||||
//int rmt_sockname_len;
|
||||
//struct sockaddr_in lcl_sockname;
|
||||
//struct sockaddr_in rmt_sockname;
|
||||
//char msr_cmnd[20];
|
||||
//struct RS__RplyStruct *rply_ptr;
|
||||
|
||||
*pData = NULL;
|
||||
|
||||
@@ -246,7 +246,7 @@
|
||||
int SerialGetSocket(void **pData)
|
||||
{
|
||||
struct SerialInfo *my_info = NULL;
|
||||
int iTmo;
|
||||
// int iTmo;
|
||||
|
||||
my_info = (struct SerialInfo *)*pData;
|
||||
assert(my_info);
|
||||
@@ -260,7 +260,7 @@
|
||||
{
|
||||
|
||||
struct SerialInfo *info_ptr;
|
||||
char buff[4];
|
||||
// char buff[4];
|
||||
|
||||
info_ptr = (struct SerialInfo *) *pData;
|
||||
if (info_ptr == NULL) return True;
|
||||
@@ -278,7 +278,7 @@
|
||||
{
|
||||
|
||||
struct SerialInfo *info_ptr;
|
||||
char buff[4];
|
||||
// char buff[4];
|
||||
|
||||
info_ptr = (struct SerialInfo *) *pData;
|
||||
if (info_ptr == NULL) return True;
|
||||
@@ -348,15 +348,17 @@
|
||||
int SerialSend(void **pData, char *pCommand)
|
||||
{
|
||||
struct SerialInfo *info_ptr;
|
||||
int status, c_len, size, max_size, ncmnds;
|
||||
int bytes_to_come, bytes_left;
|
||||
//int status, c_len, size, max_size, ncmnds;
|
||||
int status, c_len, size, ncmnds;
|
||||
//int bytes_to_come, bytes_left;
|
||||
int bytes_left;
|
||||
int iResult;
|
||||
char *nxt_byte_ptr;
|
||||
char err_text[80];
|
||||
//char *nxt_byte_ptr;
|
||||
//char err_text[80];
|
||||
char text[20];
|
||||
char *txt_ptr;
|
||||
char *cmnd_lst_ptr;
|
||||
char *pComCom = NULL;
|
||||
//char *pComCom = NULL;
|
||||
|
||||
/*
|
||||
** Do nothing if no connection - the connection gets
|
||||
@@ -443,17 +445,18 @@
|
||||
int SerialReceive(void **pData, char *pBuffer, int iBufLen)
|
||||
{
|
||||
struct SerialInfo *info_ptr;
|
||||
int status, c_len, size, max_size, ncmnds;
|
||||
int status;//, c_len,
|
||||
int size, max_size; //, ncmnds;
|
||||
int bytes_to_come, bytes_left;
|
||||
int iResult;
|
||||
char *nxt_byte_ptr;
|
||||
char err_text[80];
|
||||
char text[20];
|
||||
char *txt_ptr;
|
||||
char *cmnd_lst_ptr;
|
||||
//char err_text[80];
|
||||
//char text[20];
|
||||
//char *txt_ptr;
|
||||
//char *cmnd_lst_ptr;
|
||||
struct RS__RplyStruct_V01B *ptr = NULL;
|
||||
long lMask = 0L;
|
||||
struct timeval tmo = {0,1};
|
||||
//long lMask = 0L;
|
||||
//struct timeval tmo = {0,1};
|
||||
|
||||
|
||||
/*
|
||||
@@ -565,17 +568,18 @@
|
||||
int iBufLen, char *cTerm )
|
||||
{
|
||||
struct SerialInfo *info_ptr;
|
||||
int status, c_len, size, max_size, ncmnds;
|
||||
int status;//, c_len,
|
||||
int size, max_size;//, ncmnds;
|
||||
int bytes_to_come, bytes_left;
|
||||
int iResult;
|
||||
char *nxt_byte_ptr;
|
||||
char err_text[80];
|
||||
char text[20];
|
||||
char *txt_ptr;
|
||||
char *cmnd_lst_ptr;
|
||||
//char err_text[80];
|
||||
//char text[20];
|
||||
//char *txt_ptr;
|
||||
//char *cmnd_lst_ptr;
|
||||
struct RS__RplyStruct_V01B *ptr = NULL;
|
||||
long lMask = 0L;
|
||||
struct timeval tmo = {0,1};
|
||||
//long lMask = 0L;
|
||||
//struct timeval tmo = {0,1};
|
||||
|
||||
|
||||
/*
|
||||
@@ -889,7 +893,7 @@
|
||||
void SetSerialSleep(void **pData, SerialSleep pFun, void *pUserData)
|
||||
{
|
||||
struct SerialInfo *pInfo = NULL;
|
||||
int iRet;
|
||||
// int iRet;
|
||||
|
||||
pInfo = (struct SerialInfo *)*pData;
|
||||
pInfo->pFunc = pFun;
|
||||
|
||||
507
site_ansto/hardsup/west4100util.c
Normal file
507
site_ansto/hardsup/west4100util.c
Normal file
@@ -0,0 +1,507 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
W E S T 4 1 0 0 U T I L . C
|
||||
|
||||
A few utility functions for dealing with a WEST4100 temperature controller
|
||||
within the SINQ setup: host -- TCP/IP -- MAC --- RS-232.
|
||||
|
||||
Mark Koennecke, Juli 1997
|
||||
Mark Lesha, January 2006 (based on ITC4 code)
|
||||
Paul Barron, January 2008 (Note: This is based on the old LAKESHORE340 code and
|
||||
not the new LS340 code written by Rodney Davies Feb 08)
|
||||
|
||||
Copyright:
|
||||
|
||||
Labor fuer Neutronenstreuung
|
||||
Paul Scherrer Institut
|
||||
CH-5423 Villigen-PSI
|
||||
|
||||
|
||||
The authors hereby grant permission to use, copy, modify, distribute,
|
||||
and license this software and its documentation for any purpose, provided
|
||||
that existing copyright notices are retained in all copies and that this
|
||||
notice is included verbatim in any distributions. No written agreement,
|
||||
license, or royalty fee is required for any of the authorized uses.
|
||||
Modifications to this software may be copyrighted by their authors
|
||||
and need not follow the licensing terms described here, provided that
|
||||
the new terms are clearly indicated on the first page of each file where
|
||||
they apply.
|
||||
|
||||
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
|
||||
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
|
||||
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
|
||||
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
|
||||
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
||||
MODIFICATIONS.
|
||||
----------------------------------------------------------------------------*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <fortify.h>
|
||||
#include <sics.h>
|
||||
#include <modriv.h>
|
||||
#include <rs232controller.h>
|
||||
#include "west4100util.h"
|
||||
#include "modbustcp.h"
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
int WEST4100_Check_Status(pWEST4100 self)
|
||||
/* Can be called to check for correct operation of the WEST4100 */
|
||||
{
|
||||
int iRet, iRetry;
|
||||
unsigned char pCommand[20];
|
||||
unsigned char pReply[132];
|
||||
|
||||
iRetry=0;
|
||||
do
|
||||
{
|
||||
// Check Alarm 1
|
||||
printf("%-9s %-23s","Checking:", "Status Alarm 1........");
|
||||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x01,0x00,0x05,0x00,0x01);
|
||||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||||
return iRet;
|
||||
|
||||
if(pReply[3] != 0x0)
|
||||
{
|
||||
printf("Warning: Alarm 1 Activated\n");
|
||||
strcpy(self->pAns,pReply);
|
||||
return 1;
|
||||
}else printf("OK\n");
|
||||
|
||||
// Check Alarm 2
|
||||
printf("%-9s %-23s","Checking:", "Status Alarm 2........");
|
||||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x01,0x00,0x06,0x00,0x01);
|
||||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||||
return iRet;
|
||||
|
||||
if(pReply[3]!=0x0)
|
||||
{
|
||||
printf("Warning: Alarm 2 Activated\n");
|
||||
strcpy(self->pAns,pReply);
|
||||
return 1;
|
||||
}else{
|
||||
printf("OK\n");
|
||||
return 1;
|
||||
}
|
||||
} while((++iRetry<10));
|
||||
/* If we fell out of the loop, the WEST4100 recieved a bad response*/
|
||||
sprintf(self->pAns,"Write Status=%s",pReply);
|
||||
printf("Bad response received!\n");
|
||||
return WEST4100__BADREAD;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
int WEST4100_ConfigureAndQueryGen(pWEST4100 self, char *command, int fParAdr, float fVal, char *diagnosis)
|
||||
{
|
||||
int iRet;
|
||||
unsigned char pCommandSet[79], pCommandCheck[79];
|
||||
unsigned char pReply[79];
|
||||
unsigned char fParAdrHex[2],fValHex[2];
|
||||
|
||||
// Convert int to hexstring
|
||||
if((iRet=(int2hexstring((int)fParAdr,fParAdrHex)))==0)
|
||||
return iRet;
|
||||
if((iRet=(int2hexstring((int)fVal,fValHex)))==0)
|
||||
return iRet;
|
||||
|
||||
/* Construct a write command. */
|
||||
printf("%-9s %-23s","Setting: ",command);
|
||||
sprintf(pCommandSet,"%c%c%c%c%c%c",self->iAdr,0x06,
|
||||
fParAdrHex[0],fParAdrHex[1],fValHex[0],fValHex[1]);
|
||||
|
||||
/* Issue a write command. */
|
||||
if((iRet=transactModbusTCP(self->controller,pCommandSet,/*strlen(pCommand)*/6,pReply,79))!=1)
|
||||
return iRet;
|
||||
printf("OK\n");
|
||||
|
||||
/* Construct a read command to check that the paramater was set.*/
|
||||
printf("%-9s %-23s","Checking:",command);
|
||||
sprintf(pCommandCheck,"%c%c%c%c%c%c",self->iAdr,04,fParAdrHex[0],fParAdrHex[1],0x00,0x1);
|
||||
|
||||
/* Issue a read command .*/
|
||||
if ((iRet=transactModbusTCP(self->controller,pCommandCheck,6,pReply,79))<=0)
|
||||
{
|
||||
printf("transactRS232 error! Code=%d.\n",iRet);
|
||||
printf("DEBUG: pReply='%s' len=%d \n",pReply,strlen(pReply));
|
||||
return iRet;
|
||||
}
|
||||
|
||||
// Check that the read data is the same as that was set
|
||||
if ( (pCommandSet[4]!=pReply[3]) || (pCommandSet[5]!=pReply[4]) )
|
||||
{
|
||||
printf("Response was bad, Data not set.\n");
|
||||
if (diagnosis&&*diagnosis)
|
||||
sprintf(self->pAns,"%s response=%s (%s.)",command,pReply,diagnosis);
|
||||
else
|
||||
sprintf(self->pAns,"%s response=%s",command,pReply);
|
||||
return WEST4100__BADREAD;
|
||||
}
|
||||
printf("OK\n");
|
||||
return 1;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
int WEST4100_SetControl(pWEST4100 self, int iControl)
|
||||
{
|
||||
// Left over from lakeshore code, West only has 1 sensor to choose from.
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
int WEST4100_Setup(pWEST4100 self)
|
||||
{
|
||||
int iRet;
|
||||
unsigned char pCommand[40];
|
||||
unsigned char pReply[132];
|
||||
//int fVal = 999999.;
|
||||
|
||||
/* Check the WEST4100 status */
|
||||
if ((iRet=WEST4100_Check_Status(self))!=1)
|
||||
return iRet;
|
||||
|
||||
// Check the write status
|
||||
printf("%-9s %-23s","Checking:", "Write Status..........");
|
||||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x01,0x00,0x01,0x00,0x01);
|
||||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||||
{
|
||||
printf("Comms error!\n");
|
||||
return iRet; // Comms problem
|
||||
}
|
||||
if (pReply[3] & 0x1)
|
||||
{
|
||||
printf("OK\n");
|
||||
}else if (pReply[3] == 0x00)
|
||||
{
|
||||
printf("Status is Write Disabled.\n");
|
||||
return WEST4100__READONLY;
|
||||
}
|
||||
|
||||
/* Check that the controller is a gen-new-wine WEST4100 */
|
||||
printf("%-9s %-23s","Checking:", "ID....................");
|
||||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x03,0x00,122,0x00,0x01);
|
||||
if((iRet=transactModbusTCP(self->controller,pCommand,/*strlen*/6,pReply,79))!=1)
|
||||
return iRet;
|
||||
|
||||
if ((pReply[3]!=0x17) || (pReply[4]!=0xd4))
|
||||
{
|
||||
printf("Error: Incorrect ID\n");
|
||||
strcpy(self->pAns,pReply);
|
||||
return WEST4100__NOWEST4100;
|
||||
}
|
||||
else printf("OK\n");
|
||||
|
||||
// Set Output Limit
|
||||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Output Power to 100%..",20,100,""))!=1)
|
||||
return iRet;
|
||||
|
||||
// Set Alarm1 Limit
|
||||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Alarm1 to 1600........",13,1600,""))!=1)
|
||||
return iRet;
|
||||
|
||||
// Set Alarm2 Limit
|
||||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Alarm2 to 0...........",14,0,""))!=1)
|
||||
return iRet;
|
||||
|
||||
// Set Upper Limit
|
||||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Upper Limit to 1800...",22,1800,""))!=1)
|
||||
return iRet;
|
||||
|
||||
// Set Lower Limit
|
||||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Lower Limit to 0......",23,0,""))!=1)
|
||||
return iRet;
|
||||
|
||||
// Set Ramp Rate
|
||||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Ramp Rate to 0ff......",24,10000,""))!=1)
|
||||
return iRet;
|
||||
|
||||
/* Check the WEST4100 operating status one last time */
|
||||
if ((iRet=WEST4100_Check_Status(self))!=1)
|
||||
return iRet;
|
||||
|
||||
return 1; /* Success */
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
int WEST4100_Open(pWEST4100 *pData, char *pRS232, int iAddress, int iTransaction)
|
||||
{
|
||||
pWEST4100 self = NULL;
|
||||
|
||||
self = (pWEST4100)malloc(sizeof(WEST4100));
|
||||
if(self == NULL)
|
||||
{
|
||||
return WEST4100__BADMALLOC;
|
||||
}
|
||||
*pData = self;
|
||||
self->iAdr = iAddress;
|
||||
self->iTransact = iTransaction;
|
||||
|
||||
self->controller = NULL;
|
||||
|
||||
self->controller = (prs232)FindCommandData(pServ->pSics,pRS232,
|
||||
"RS232 Controller");
|
||||
if(!self->controller){
|
||||
/*SCWrite(pCon,"ERROR: motor controller not found",eError); */
|
||||
return WEST4100__BADCOM;
|
||||
}
|
||||
|
||||
return WEST4100_Setup(self);
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
void WEST4100_Close(pWEST4100 *pData)
|
||||
{
|
||||
pWEST4100 self;
|
||||
|
||||
self = *pData;
|
||||
if (!self)
|
||||
return; // Just in case
|
||||
|
||||
return;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int WEST4100_Config(pWEST4100 *pData, int iTmo, int iRead, int iControl)
|
||||
{
|
||||
pWEST4100 self;
|
||||
|
||||
self = *pData;
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int WEST4100_Send(pWEST4100 *pData, char *pCommand, char *pReply, int iLen)
|
||||
{
|
||||
int iRet;
|
||||
pWEST4100 self;
|
||||
|
||||
self = *pData;
|
||||
|
||||
char *ptr = pCommand;
|
||||
unsigned int byte;
|
||||
unsigned char pCommandHex[79];
|
||||
size_t i;
|
||||
|
||||
// Convert char string command to hex string with every two characters concatenated to one array field
|
||||
for (i=0;i<sizeof pCommandHex ;++i)
|
||||
{
|
||||
if(sscanf(ptr,"%2x",&byte)!=1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pCommandHex[i]=byte;
|
||||
ptr +=2;
|
||||
}
|
||||
|
||||
// Issue hex command
|
||||
printf("%s ","Issuing Send: ");
|
||||
if((iRet=transactModbusTCP(self->controller,pCommandHex,6,pReply,79))!=1){
|
||||
printf("%-s","Response: ");
|
||||
displayHexString(pReply);
|
||||
return iRet;
|
||||
}
|
||||
printf("OK\n");
|
||||
|
||||
printf("%-s","Response: ");
|
||||
displayHexString(pReply);
|
||||
|
||||
/* Check the WEST4100 operating status after issuing the command, if it was successful */
|
||||
if (iRet>=1)
|
||||
iRet=WEST4100_Check_Status(self);
|
||||
|
||||
return iRet;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int WEST4100_Read(pWEST4100 *pData, float *fVal)
|
||||
{
|
||||
unsigned char pCommand[20], pReply[132];
|
||||
int iRet;
|
||||
float fRead = -999999.;
|
||||
pWEST4100 self;
|
||||
|
||||
self = *pData;
|
||||
|
||||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,04,0x0,0x1,0x0,0x1);
|
||||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||||
{
|
||||
printf("transactRS232 error! Code=%d.\n",iRet);
|
||||
printf("DEBUG: pReply='%s' len=%d \n",pReply,strlen(pReply));
|
||||
return iRet;
|
||||
}
|
||||
|
||||
// Because a value read will never be greater than FF FF we can use a simple line to convert
|
||||
fRead=(256*pReply[3])+pReply[4];
|
||||
|
||||
if(fRead > 65535 || fRead < 0) // Not a number, probably an error response
|
||||
{
|
||||
return WEST4100__BADREAD;
|
||||
}
|
||||
|
||||
*fVal = fRead;
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
int WEST4100_Query(pWEST4100 *pData, int parAddress, int *parValue)
|
||||
{
|
||||
unsigned char pCommand[20], pReply[132], pAddress[1];
|
||||
int iRet;
|
||||
pWEST4100 self;
|
||||
|
||||
self = *pData;
|
||||
|
||||
int2hexstring(parAddress,pAddress);
|
||||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x4,pAddress[0],pAddress[1],0x0,0x1);
|
||||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||||
{
|
||||
printf("transactRS232 error! Code=%d.\n",iRet);
|
||||
printf("DEBUG: pReply='%s' len=%d \n",pReply,strlen(pReply));
|
||||
return iRet;
|
||||
}
|
||||
|
||||
// Because a value read will never be greater than FF FF we can use a simple line to convert
|
||||
*parValue=(256*pReply[3])+pReply[4];
|
||||
|
||||
if(*parValue > 65535 || *parValue < 0) // Not a number, probably an error response
|
||||
{
|
||||
return WEST4100__BADREAD;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
int WEST4100_Write(pWEST4100 *pData, int parAddress, int parValue)
|
||||
{
|
||||
unsigned char displaytext[20];
|
||||
int iRet;
|
||||
pWEST4100 self;
|
||||
|
||||
self = *pData;
|
||||
|
||||
sprintf(displaytext,"Parameter Number %d...",parAddress);
|
||||
if((iRet=WEST4100_ConfigureAndQueryGen(self,displaytext,parAddress,parValue,""))!=1)
|
||||
return iRet;
|
||||
|
||||
if ((iRet=WEST4100_Check_Status(self))!=1)
|
||||
return iRet;
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
int WEST4100_Set(pWEST4100 *pData, float fVal)
|
||||
{
|
||||
int iRet, i;
|
||||
pWEST4100 self;
|
||||
|
||||
self = *pData;
|
||||
|
||||
for(i = 0; i < 3; i++)
|
||||
{
|
||||
// Set setpoint
|
||||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Setpoint...",0x02,fVal,""))!=1)
|
||||
return iRet;
|
||||
|
||||
printf("SETP OK, checking status and returning.\n");
|
||||
iRet=WEST4100_Check_Status(self);
|
||||
|
||||
return iRet;
|
||||
}
|
||||
printf("SETP failed!\n");
|
||||
return WEST4100__BADSET;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
void WEST4100_ErrorTxt(pWEST4100 *pData,int iCode, char *pError, int iLen)
|
||||
{
|
||||
char pBueffel[512];
|
||||
pWEST4100 self;
|
||||
|
||||
self = *pData;
|
||||
|
||||
switch(iCode)
|
||||
{
|
||||
case WEST4100__BADCOM:
|
||||
sprintf(pBueffel,"WEST4100: Invalid command or offline, got %s",
|
||||
self->pAns);
|
||||
strncpy(pError,pBueffel,iLen);
|
||||
break;
|
||||
case WEST4100__BADPAR:
|
||||
strncpy(pError,"WEST4100: Invalid parameter specified",iLen);
|
||||
break;
|
||||
case WEST4100__BADMALLOC:
|
||||
strncpy(pError,"WEST4100: Error allocating memory in WEST4100",iLen);
|
||||
break;
|
||||
case WEST4100__BADREAD:
|
||||
strncpy(pError,"WEST4100: Badly formatted answer",iLen);
|
||||
break;
|
||||
case WEST4100__BADSET:
|
||||
strncpy(pError,"WEST4100: Failed three times to write new set value to WEST4100",iLen);
|
||||
break;
|
||||
case WEST4100__FAULT: // Covers various WEST4100 self-diagnosed fault conditions
|
||||
sprintf(pBueffel,"WEST4100: Internal fault condition detected: %s",self->pAns);
|
||||
strncpy(pError,pBueffel,iLen);
|
||||
break;
|
||||
case WEST4100__NOWEST4100:
|
||||
sprintf(pBueffel,"WEST4100: Wrong model number (driver is for Model 340 only): %s",self->pAns);
|
||||
strncpy(pError,pBueffel,iLen);
|
||||
break;
|
||||
default:
|
||||
getRS232Error(iCode, pError,iLen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
int int2hexstring(int fVal, unsigned char *hexstring)
|
||||
{
|
||||
size_t k;
|
||||
int fValInt;
|
||||
unsigned char temp[79];
|
||||
int result,remainder,index,index2;
|
||||
|
||||
fValInt=fVal;
|
||||
|
||||
if(fValInt>65535)
|
||||
{
|
||||
printf("Value greater than FF FF");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert integer to hex and putting each char in an array
|
||||
memset(temp,0,sizeof(temp));
|
||||
result=1;
|
||||
for(k=0;result!=0;k++)
|
||||
{
|
||||
result=fValInt/16;
|
||||
remainder=fValInt%16;
|
||||
fValInt=result;
|
||||
temp[k]=remainder;
|
||||
}
|
||||
|
||||
// Formatting a new array so that there is one byte per array field
|
||||
if((k%2)==0)
|
||||
index2=k/2-1;
|
||||
else
|
||||
index2=k/2;
|
||||
|
||||
if(fVal>255)
|
||||
{
|
||||
for(index=0;index2>=0;(index=index+2),index2--)
|
||||
{
|
||||
hexstring[index2]=(temp[index+1]*16)+temp[index];
|
||||
}
|
||||
}else{
|
||||
hexstring[0]=0x0;
|
||||
hexstring[1]=(temp[1]*16)+temp[0];
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
void displayHexString(unsigned char *hexstring)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;(i<5)|(hexstring[i]!='\0');i++)printf("%02x ",hexstring[i]);
|
||||
printf("\n");
|
||||
}
|
||||
130
site_ansto/hardsup/west4100util.h
Normal file
130
site_ansto/hardsup/west4100util.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/*---------------------------------------------------------------------------
|
||||
W E S T 4 1 0 0
|
||||
|
||||
A few utility functions for talking to a Lakeshore 340
|
||||
temperature controller via the SINQ setup: TCP/IP--MAC--RS-232--
|
||||
WEST4100.
|
||||
|
||||
Mark Koennecke, Juli 1997
|
||||
Mark Lesha, January 2006 (based on ITC4 code)
|
||||
Paul Barron, January 2008 (Note: This is based on the old LAKESHORE340 code and
|
||||
not the new LS340 code written by Rodney Davies Feb 08)
|
||||
|
||||
----------------------------------------------------------------------------*/
|
||||
#ifndef SINQWEST4100
|
||||
#define SINQWEST4100
|
||||
|
||||
/*----------------------- ERRORCODES--------------------------------------
|
||||
Most functions return a negative error code on failure. Error codes
|
||||
defined are those defined for serialsinq plus a few additional ones:
|
||||
*/
|
||||
|
||||
#define WEST4100__BADCOM -501
|
||||
/* command not recognized */
|
||||
#define WEST4100__BADPAR -502
|
||||
/* bad parameter to command */
|
||||
#define WEST4100__BADMALLOC -503
|
||||
/* error allocating memory */
|
||||
#define WEST4100__BADREAD -504
|
||||
/* error analysing command string on Read */
|
||||
#define WEST4100__FAULT -505
|
||||
/* fault or overload condition exists in WEST4100 */
|
||||
#define WEST4100__NOWEST4100 -510
|
||||
/* Controller is not WEST4100 */
|
||||
#define WEST4100__BADSET -530
|
||||
/* failed three times to set temperature */
|
||||
#define WEST4100__READONLY -531
|
||||
/*------------------------------------------------------------------------*/
|
||||
typedef struct __WEST4100 {
|
||||
int iAdr;
|
||||
int iTransact;
|
||||
void *pData;
|
||||
char pAns[80];
|
||||
prs232 controller;
|
||||
} WEST4100;
|
||||
|
||||
typedef struct __WEST4100 *pWEST4100;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
int WEST4100_Open(pWEST4100 *pData,char *pHost, int iAddress, int iTransaction);
|
||||
/***** creates an WEST4100 datastructure and opens a connection to the WEST4100
|
||||
controller. Input Parameters are:
|
||||
the hostname
|
||||
the port number
|
||||
the RS-232 channel number on the Mac.
|
||||
iMode: 1 for ReadOnly, 0 for normal mode
|
||||
|
||||
Return values are 1 for success, a negative error code on
|
||||
failure.
|
||||
|
||||
*/
|
||||
|
||||
void WEST4100_Close(pWEST4100 *pData);
|
||||
/****** close a connection to an WEST4100controller and frees its
|
||||
data structure. The only parameter is a pointer to the data
|
||||
structure for this controller. This pointer will be invalid after
|
||||
this call.
|
||||
*/
|
||||
|
||||
int WEST4100_Config(pWEST4100 *pData, int iTmo, int iRead,
|
||||
int iControl);
|
||||
/***** configure some aspects of a WEST4100temperature controller.
|
||||
The parameter are:
|
||||
- a pointer to the data structure for the controller as
|
||||
returned by WEST4100_Open
|
||||
- a value for the connection timeout
|
||||
- the temperature sensor to use for reading the
|
||||
temperature.
|
||||
- the temperature sensor used by the WEST4100controller
|
||||
for regulating the temperature.
|
||||
- the divisor needed to calculate the real temperature
|
||||
from the sensor.
|
||||
The function returns 1 on success, a negative error code on
|
||||
failure.
|
||||
*/
|
||||
|
||||
int WEST4100_Send(pWEST4100 *pData, char *pCommand, char *pReply, int iLen);
|
||||
/******* send a the command in pCommand to the WEST4100controller.
|
||||
A possible reply is returned in the buffer pReply.
|
||||
Maximum iLen characters are copied to pReply.
|
||||
The first parameter is a pointer to a WEST4100data structure
|
||||
as returned by WEST4100_Open.
|
||||
|
||||
Return values are 1 for success, a negative error code on
|
||||
failure.
|
||||
*/
|
||||
|
||||
int WEST4100_Read(pWEST4100 *pData, float *fVal);
|
||||
/******* reads the current actual temperature of the sensor
|
||||
configured by ConfigWEST4100for reading. The value is returned
|
||||
in fVal. The first parameter is a pointer to a WEST4100
|
||||
data structure as returned by WEST4100_Open.
|
||||
|
||||
Return values are 1 for success, a negative error code on
|
||||
failure.
|
||||
*/
|
||||
|
||||
int WEST4100_Set(pWEST4100 *pData, float fVal);
|
||||
/****** sets a new preset temperature in the WEST4100temperature
|
||||
controller. Parameters are:
|
||||
- a pointer to a WEST4100data structure as returned by WEST4100_Open.
|
||||
- the new preset value.
|
||||
|
||||
Return values are 1 for success, a negative error code on
|
||||
failure.pEVInterface
|
||||
*/
|
||||
|
||||
void WEST4100_ErrorTxt(pWEST4100 *pData, int iCode, char *pError, int iLen);
|
||||
/******* translates one of the negative error WEST4100error codes
|
||||
into text. Maximum iLen bytes will be copied to the
|
||||
buffer pError;
|
||||
*/
|
||||
|
||||
int WEST4100_Query(pWEST4100 *pData, int parAddress, int *parValue);
|
||||
int WEST4100_Write(pWEST4100 *pData, int parAddress, int parValue);
|
||||
int int2hexstring(int fVal, unsigned char *hexstring);
|
||||
void displayHexString(unsigned char *hexstring);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user