/*
 * Copyright (c) 2002-2007 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: vzf_tests.c 15597 2012-03-28 22:21:32Z jomi.gregory $
 */

#include "dagapi.h"
#include "dagclarg.h"
#include "dagerf.h"

#include "vzf_tests.h"

#define ERF_HEADER	16	// bytes
#define ERF_EXT_HEADER	8	// bytes

/* Command Line Arguments constants */
enum
{
	CLA_HELP,
	CLA_MODE,
	CLA_INTERFACES,
	CLA_SONET_SEQUENCE,
	CLA_SONET_POINTERS,
	CLA_SONET_FLAGS,
	CLA_SONET_B_ERRORS
};

// Global test definitions
#define VZF_SONET_SEQUENCE 0x00000001
#define VZF_SONET_POINTERS 0x00000002
#define VZF_SONET_FLAGS    0x00000004
#define VZF_SONET_B_ERRORS 0x00000008

#define TEST_COUNT 4

char* test_names[] =
{
	"sonet_sequence",
	"sonet_pointers",
	"sonet_flags",
	"sonet_b_errors"
};

static uint32_t tests = 0;

/* Global counters for final reporting */
typedef struct test_counters {
	char     ran;
	uint32_t passed;
	uint32_t failed;
	uint32_t ignored;
	uint32_t warning;
	uint32_t unknown;
} test_counters_t;

static uint32_t vzf_total = 0;
static uint32_t vzf_failed = 0;
static uint32_t vzf_ignored = 0;
static uint32_t vzf_warning = 0;

test_counters_t vzf_counter[TEST_COUNT];

static char rec_msg[MAX_STR_LEN];
static int rec_msg_len = 0;

/* Processor mode constants */
enum
{
	SONET_MODE,
	DS3_MODE,
	T1_E1_MODE
};

/* Processor network rate constants */
enum
{
	RATE_INVALID = -1,
	RATE_OC1_STM0 = 0x0,
	RATE_OC3_STM1 = 0x1,
	RATE_OC12_STM4 = 0x2,
	RATE_OC48_STM16 = 0x3,
	RATE_OC192_STM64 = 0x4,
	RATE_OC768_STM256 = 0x5	
};

// SONET extension header variables
static uint64_t raw_link_hdr = 0;
static uint64_t sdh_frame_info_hdr = 0;
static uint64_t spe_frame_info_hdr = 0;

// SONET test counters
static uint32_t ptr_no_activity_count[MAX_INTERFACES];
static uint32_t ptr_increment_count[MAX_INTERFACES];
static uint32_t ptr_decrement_count[MAX_INTERFACES];
static uint32_t	new_ptr_no_ndf_count[MAX_INTERFACES];
static uint32_t	new_ptr_valid_ndf_count[MAX_INTERFACES];
static uint32_t	ptr_invalid_count[MAX_INTERFACES];
static uint32_t b1_errors_counter[MAX_INTERFACES];
static uint32_t b2_errors_counter[MAX_INTERFACES];
static uint32_t b3_errors_counter[MAX_INTERFACES];

/* Internal Function prototypes */
static void print_usage(ClArgPtr clarg);
static int get_test_mode(char *mode);
static int process_sonet_extension_headers(char *rec, int len, uint32_t ext_hdr_count);
static void increment_counters(int res, test_counters_t *cntr);
static int test_sonet_sequence(char *rec, int len, int num);
static int test_sonet_pointers(char *rec, int len, int num);
static int test_sonet_flags(char *rec, int len, int num);
static int test_sonet_b_errors(char *rec, int len, int num);

/* Global variables for this test */
static uint8_t mode = 0;
static uint32_t expected_ext_hdr_count = 0;
static int ifaces_count = 1;
static uint8_t iface;

/* Global tests definitions */

/* Global tests configuration */


//---------------------------------------------------------------------------------------------------------------------

/* Main initialization of the test */
int vzf_tests_init(char *params[], int param_cnt, global_params_t *global_params, test_printf f)
{

	ClArgPtr clarg = NULL;
	FILE* errorfile = NULL;
	int argindex = 0;
	int clarg_result = 0;
	int code = 0;
	char mode_buf[256];
	uint8_t i;

	/* Set up the command line options. */
	clarg = dagclarg_init(param_cnt, (const char* const *) params);
	
	/* General options. */
	dagclarg_add(clarg, "This page", "--help", 'h', CLA_HELP);
	dagclarg_add_string(clarg, "Select the test mode (accepts 'sonet', 'ds3' or 't1_e1', default: sonet)", "--mode", 'm', "n", mode_buf, 256, CLA_MODE);
	dagclarg_add_int(clarg, "Number of interfaces to test (default: 1, maximum: 16)", "--interfaces", 'i', "n", &ifaces_count, CLA_INTERFACES);
	dagclarg_add(clarg, "Check SONET sequence numbers (only at OC48 and OC192, use with --mode option)", "--sequence", 'q', CLA_SONET_SEQUENCE);
	dagclarg_add(clarg, "Count SONET pointer activity and movements (use with --mode option)", "--pointers", 'p', CLA_SONET_POINTERS);
	dagclarg_add(clarg, "Check SONET error condition flags (use with --mode option)", "--sonet_flags", 's', CLA_SONET_FLAGS);
	dagclarg_add(clarg, "Check SONET B1/B2/B3 errors (use with --mode option)", "--b_errors", 'b', CLA_SONET_B_ERRORS);

	/* Parse the command line options. */
	clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
			case CLA_HELP:
				print_usage(clarg);
				return TEST_HELP;

			case CLA_MODE:
				mode = get_test_mode(mode_buf);
				break;

			case CLA_INTERFACES:
				// Do nothing
				break;

			case CLA_SONET_SEQUENCE:
				tests |= VZF_SONET_SEQUENCE;
				break;

			case CLA_SONET_POINTERS:
				tests |= VZF_SONET_POINTERS;
				break;

			case CLA_SONET_FLAGS:
				tests |= VZF_SONET_FLAGS;
				break;

			case CLA_SONET_B_ERRORS:
				tests |= VZF_SONET_B_ERRORS;
				break;

			default:
				if (params[argindex][0] == '-')
				{
					/* Unknown option. */
					dagutil_error("Unknown option %s\n", params[argindex]);
					dagclarg_display_usage(clarg, stderr);
					return TEST_FAIL;
				}
				break;
		}
		clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}

	/* Check for errors in the arguments */
	if (-1 == clarg_result)
	{
		if (argindex < param_cnt)
		{
			dagutil_error("while processing option %s\n", params[argindex]);
		}
		dagclarg_display_usage(clarg, stderr);
		return TEST_FAIL;
	}

	// Set the number of expected extension headers
	if (mode == SONET_MODE)
		expected_ext_hdr_count = 3;
	else if (mode == DS3_MODE)
		expected_ext_hdr_count = 4;
	else if (mode == T1_E1_MODE)
		expected_ext_hdr_count = 0;
	else
	{
		dagutil_warning("Unknown network mode: %s\n", mode_buf);
		return TEST_FAIL;
	}	

	/* Initialize local variables */
	memset(&vzf_counter, 0, sizeof(vzf_counter));

	for (i = 0; i < MAX_INTERFACES; i++)
	{
		ptr_no_activity_count[i] = 0;
		ptr_increment_count[i] = 0;
		ptr_decrement_count[i] = 0;
		new_ptr_no_ndf_count[i] = 0;
		new_ptr_valid_ndf_count[i] = 0;
		b1_errors_counter[i] = 0;
		b2_errors_counter[i] = 0;
		b3_errors_counter[i] = 0;
	}

	return TEST_PASS;
}

/* This is the main test function.
 *
 * This function basically calls the correct function for testing
 * and increments the correct counters depending on the test result.
 *
 */
int vzf_tests_run(char *rec, int len, char* lastpkt, struct_protocol_t prot, uint64_t rec_cnt)
{
	int ret_val = TEST_PASS;
	int tmp_val = 0;
	dag_record_t* drec = (dag_record_t*) rec;
	uint32_t ext_hdr_count = 0;

	rec_msg_len = 0;

	// Retrieve interface information from the ERF header
	iface = drec->flags.iface;
	if ((iface >= MAX_INTERFACES))
	{
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf: Incorrect interface value detected: %d (max interfaces: %d)\n", iface, MAX_INTERFACES);
		return TEST_FAIL;
	}

#if 1
	// FIXME THIS WILL WORK FOR SONET AND DS3, BUT NOT T1/E1
	// Check for Raw Link ERF type
	if ((drec->type & 0x7f) != ERF_TYPE_RAW_LINK)
	{
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf: Incorrect ERF type found on interface %d: %d (expected raw link erf type: %d)\n", iface, drec->type, ERF_TYPE_RAW_LINK);
		return TEST_FAIL;
	}
#endif

	if (mode == SONET_MODE)
	{
		ext_hdr_count = dagerf_ext_header_count((uint8_t*)drec, len);
		if (process_sonet_extension_headers(rec, len, ext_hdr_count) != TEST_PASS)
			return TEST_FAIL;

		if (tests & VZF_SONET_SEQUENCE)
		{
			tmp_val |= test_sonet_sequence(rec, len, 0);
			increment_counters(tmp_val, &vzf_counter[0]);
			ret_val |= tmp_val;
		}

		if (ext_hdr_count == expected_ext_hdr_count)
		{
			// Run tests
			if (tests & VZF_SONET_POINTERS)
			{
				tmp_val |= test_sonet_pointers(rec, len, 1);
				increment_counters(tmp_val, &vzf_counter[1]);
				ret_val |= tmp_val;
			}
	
			if (tests & VZF_SONET_FLAGS)
			{
				tmp_val |= test_sonet_flags(rec, len, 2);
				increment_counters(tmp_val, &vzf_counter[2]);
				ret_val |= tmp_val;
			}
	
			if (tests & VZF_SONET_B_ERRORS)
			{
				tmp_val |= test_sonet_b_errors(rec, len, 3);
				increment_counters(tmp_val, &vzf_counter[3]);
				ret_val |= tmp_val;
			}
		}
	}

	if (mode == DS3_MODE)
	{
		// Do nothing for now, should check for valid tests
	}

	if (mode == T1_E1_MODE)
	{
		// Do nothing for now, should check for valid tests
	}

	return ret_val;
}

int vzf_tests_err_msg(char *buf, int size)
{
    if (size > 0)
    {
	    strncpy(buf, rec_msg, size);
	    buf[size-1] = '\0'; /* Just in case the buffer was too short...*/
    }
	return TEST_PASS;
}

int vzf_tests_final_msg(char *buf, int size)
{
	char msg[MAX_STR_LEN];
	int used_len = 0;
	int count;

	if (tests & VZF_SONET_POINTERS)
	{
		for (count = 0; count < ifaces_count; count++)
		{
			used_len += snprintf(&msg[used_len], MAX_STR_LEN - used_len,
						"vzf_sonet_pointers%d: %u no_activity, %u increment, %u decrement, %u new_ptr_no_ndf, %u new_ptr_valid_ndf, %u invalid\n",
						count,
						ptr_no_activity_count[count],
						ptr_increment_count[count],
						ptr_decrement_count[count],
						new_ptr_no_ndf_count[count],
						new_ptr_valid_ndf_count[count],
						ptr_invalid_count[count]);
		}
	}

	if (tests & VZF_SONET_B_ERRORS)
	{
		for (count = 0; count < ifaces_count; count++)
		{
			used_len += snprintf(&msg[used_len], MAX_STR_LEN - used_len,
						"vzf_sonet_b_errors%d: %u b1, %u b2, %u b3\n",
						count,
						b1_errors_counter[count],
						b2_errors_counter[count],
						b3_errors_counter[count]);
		}
	}

	for (count = 0; (used_len < MAX_STR_LEN) && (count < TEST_COUNT); count++)
	{
		if (vzf_counter[count].ran)
		{
			used_len += snprintf(&msg[used_len], MAX_STR_LEN - used_len,
						"vzf_%s: %u passed, %u failed, %u ignored\n",
						test_names[count],
						vzf_counter[count].passed,
						vzf_counter[count].failed,
						vzf_counter[count].ignored);
		}
	}

    if (size > 0)
    {
	    strncpy(buf, msg, size);
	    buf[size-1] = '\0';
    }

	if ((vzf_failed > 0) || (vzf_total == 0))
		return TEST_FAIL;
	else if ((vzf_warning > 0) || (vzf_ignored > 0))
		return TEST_WARNING;
	else
		return TEST_PASS;
}

int vzf_tests_cleanup()
{
	return TEST_PASS;
}

int vzf_tests_printf (char *format, ...)
{
	printf ("%s: %s - Test printf\n", __FILE__, __FUNCTION__);
	return TEST_PASS;
}

//---------------------------------------------------------------------------------------------------------------------

static void print_usage(ClArgPtr clarg)
{
	printf("vzf_tests - Endace DAG plugin library for the dagbits test utility.\n");
	printf("Usage: dagbits -d <device> --plugin vzf_tests:\"[options]\"\n");
	dagclarg_display_usage(clarg, stdout);
}

static int get_test_mode (char *mode)
{
	if (strcmp(mode, "sonet") == 0)
		return SONET_MODE;
	else if (strcmp(mode, "ds3") == 0)
		return DS3_MODE;
	else if (strcmp(mode, "t1_e1") == 0)
		return T1_E1_MODE;
	else
		return -1;
}

static int test_sonet_sequence(char *rec, int len, int num)
{
	uint8_t more_fragment;
	uint16_t sequence_num;
	static uint8_t run_test[MAX_INTERFACES];
	static uint8_t sequence_max[MAX_INTERFACES];
	static uint8_t sequence_num_previous[MAX_INTERFACES];
	static uint8_t rate[MAX_INTERFACES];

	more_fragment = (raw_link_hdr >> 55) & 0x1;
	sequence_num = (raw_link_hdr >> 16) & 0xffff;

	// For the first packet, set up the maximum sequence value and just store the sequence number.
	if (!run_test[iface])
	{
		rate[iface] = (raw_link_hdr >> 8) & 0xff;
		if (rate[iface] == RATE_OC48_STM16)
			sequence_max[iface] = 4;
		else if (rate[iface] == RATE_OC192_STM64)
			sequence_max[iface] = 16;
		else
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: Invalid rate for SONET sequence test: 0x%x (expected OC48 (0x3) or OC192 (0x4))", test_names[num], rate[iface]);
			return TEST_FAIL;
		}

		sequence_num_previous[iface] = sequence_num;
		run_test[iface] = 1;
		return TEST_PASS;
	}

	if ((rate[iface] == RATE_OC48_STM16) || (rate[iface] == RATE_OC192_STM64))
	{
		if (sequence_num == 0)
		{
#if 0
			printf("A: interface %d: sequence number: %d, previous: %d, more fragment: %d\n",
				iface,
				sequence_num,
				sequence_num_previous[iface],
				more_fragment);
#endif
	
			if (sequence_num_previous[iface] != (sequence_max[iface] - 1))
			{
				rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: interface %d: incorrect sequence number found: %d (expected: %d)\n",
					test_names[num],
					iface,
					sequence_num,
					sequence_max[iface] - 1);
				return TEST_FAIL;
			}

			// Verify the 'more fragment' field
			if (more_fragment != 1)
			{
				rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: interface %d: invalid 'more fragment' found for sequence number 0: %d (expected: 1)\n",
					test_names[num],
					iface,
					more_fragment);
				return TEST_FAIL;
			}
	
			sequence_num_previous[iface] = sequence_num;
		}	
		else if ((sequence_num > 0) && (sequence_num < sequence_max[iface]))
		{
#if 0
			printf("B: interface %d: sequence number: %d, previous: %d, more fragment: %d\n",
				iface,
				sequence_num,
				sequence_num_previous[iface],
				more_fragment);
#endif
	
			if (sequence_num != (sequence_num_previous[iface] + 1))
			{
				rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: interface %d: incorrect sequence number found: %d (expected: %d)\n",
					test_names[num],
					iface,
					sequence_num,
					sequence_num_previous[iface] == (sequence_max[iface] - 1) ? 0 : sequence_num_previous[iface] + 1);
				return TEST_FAIL;
			}
	
			// Verify the 'more fragment' field
			if (sequence_num == (sequence_max[iface] - 1))
			{
				if (more_fragment != 0)
				{
					rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: interface %d: invalid 'more fragment' found for sequence number %d: %d (expected: 0)\n",
						test_names[num],
						iface,
						sequence_num,
						more_fragment);
					return TEST_FAIL;
				}
			}
			else
			{
				if (more_fragment != 1)
				{
					rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: interface %d: invalid 'more fragment' found for sequence number %d: %d (expected: 1)\n",
						test_names[num],
						iface,
						sequence_num,
						more_fragment);
					return TEST_FAIL;
				}
			}
	
			sequence_num_previous[iface] = sequence_num;
			return TEST_PASS;
		}
		else	// Records with invalid sequence numbers
		{
#if 0
			printf("C: interface %d: sequence number: %d, previous: %d, more fragment: %d\n",
				iface,
				sequence_num,
				sequence_num_previous[iface],
				more_fragment);
#endif
	
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: interface %d: invalid sequence number found: %d (max: %d)\n",
				test_names[num],
				iface,
				sequence_num,
				sequence_max[iface]);

			return TEST_FAIL;
		}
	}

	return TEST_PASS;
}

static int test_sonet_pointers(char *rec, int len, int num)
{
	uint8_t spe_ptr_activity;
	int ret_val = TEST_PASS;

	spe_ptr_activity = (uint8_t)((spe_frame_info_hdr >> 29) & 0x7);

 	if (spe_ptr_activity == 0x0)
		++ptr_no_activity_count[iface];
	else if (spe_ptr_activity == 0x1)
		++ptr_increment_count[iface];
	else if (spe_ptr_activity == 0x2)
		++ptr_decrement_count[iface];
	else if (spe_ptr_activity == 0x4)
		++new_ptr_no_ndf_count[iface];
	else if (spe_ptr_activity == 0x5)
		++new_ptr_valid_ndf_count[iface];
	else
	{
		++ptr_invalid_count[iface];
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: Invalid pointer found: 0x%x", test_names[num], spe_ptr_activity);
		ret_val = TEST_FAIL;
	}

	return ret_val;
}

static int test_sonet_flags(char *rec, int len, int num)
{
	uint16_t flags;
	int ret_val = TEST_PASS;

	flags = ((sdh_frame_info_hdr >> 18) & 0x03ff);

 	if (flags != 0x0)
	{
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len,
			"vzf_%s: Error condition detected: flags [rdi-l:%d rdi-p:%d rei-l:%d rei-p:%d ais-l:%d ais-p:%d oof:%d lof:%d lop:%d los:%d]",
			test_names[num],
			(flags >> 2) & 1,
			flags & 1,
			(flags >> 3) & 1,
			(flags >> 1) & 1,
			(flags >> 4) & 1,
			(flags >> 9) & 1,
			(flags >> 8) & 1,
			(flags >> 7) & 1,
			(flags >> 6) & 1,
			(flags >> 5) & 1);
		ret_val = TEST_FAIL;
	}

	return ret_val;
}

static int test_sonet_b_errors(char *rec, int len, int num)
{
	uint8_t b1_error_event = 0;
	uint8_t b2_error_event = 0;
	uint8_t type = 0;
	uint8_t bip8 = 0;
	uint8_t b3 = 0;
	static uint8_t bip8_previous[MAX_INTERFACES];
	static uint8_t run_test[MAX_INTERFACES];
	uint8_t i;
	uint8_t xor_result = 0;
	int ret_val = TEST_PASS;

	// Accumulate B1/B2 errors
	b1_errors_counter[iface] += ((sdh_frame_info_hdr >> 12) & 0x0f);
	b2_errors_counter[iface] += ((sdh_frame_info_hdr >> 4) & 0x7f);

	b1_error_event = ((sdh_frame_info_hdr >> 11) & 0x01);
	b2_error_event = (sdh_frame_info_hdr & 0x01);

	if ((b1_error_event != 0x0) || (b2_error_event != 0x0))
	{
		rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: %s error event occurred", test_names[num], b1_error_event ? "B1" : "B2");
		ret_val = TEST_FAIL;
	}

	// Accumulate B3 errors only in SONET Path Mode (Type 2) or SDH Path Mode (Type 3).
	type = raw_link_hdr & 0xff;
	if (type == 0x2 || type == 0x3)
	{
		bip8 = (spe_frame_info_hdr >> 32) & 0xff;

		// Perform the B3 and BIP8 comparison starting from the third record.
		if (run_test[iface] >= 2)
		{
			b3 = (spe_frame_info_hdr >> 40) & 0xff;
			if (b3 != bip8_previous[iface])
			{
				// Count the number of B3/BIP8 errors
				xor_result = b3 ^ bip8_previous[iface];
				for (i = 0; i < 8; i++)
				{
					if (((xor_result >> i) & 0x1) == 1)
						++b3_errors_counter[iface];
				}

				rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf_%s: interface: %d, incorrect BIP8 and B3 comparison (previous BIP8: 0x%02x, B3: 0x%02x)",
					test_names[num],
					iface,
					bip8_previous[iface],
					b3);
	
				ret_val = TEST_FAIL;
			}
		}
		else
			++run_test[iface];

		bip8_previous[iface] = bip8;
	}

	return ret_val;
}

static int process_sonet_extension_headers(char *rec, int len, uint32_t ext_hdr_count)
{
	uint64_t ext_hdr_temp = 0;
	uint8_t ext_hdr_type;
	uint8_t count;

	for (count = 0; count < ext_hdr_count; count++)
	{
		ext_hdr_temp = *((uint64_t *)(&rec[ERF_HEADER + (ERF_EXT_HEADER * count)]));
		ext_hdr_temp = bswap_64(ext_hdr_temp);

		// Check extension header type codes
		ext_hdr_type = (ext_hdr_temp >> 56) & 0x7f;
		if (ext_hdr_type == 0x5)
			raw_link_hdr = ext_hdr_temp;
		else if (ext_hdr_type == 0x7)
			sdh_frame_info_hdr = ext_hdr_temp;
		else if (ext_hdr_type == 0x8)
			spe_frame_info_hdr = ext_hdr_temp;
		else
		{
			rec_msg_len += snprintf(&rec_msg[rec_msg_len], MAX_STR_LEN-rec_msg_len, "vzf: Incorrect extension header type code found: 0x%x (expected 0x5, 0x7 or 0x8)\n", ext_hdr_type);
			return TEST_FAIL;
		}
	}

	return TEST_PASS;
}

static void increment_counters(int res, test_counters_t *cntr)
{
	vzf_total++;
	cntr->ran = 1;

	switch (res)
	{
		case TEST_PASS:
			cntr->passed++;
			break;

		case TEST_IGNORE:
			vzf_ignored++;
			cntr->ignored++;
			break;

		case TEST_FAIL:
			vzf_failed++;
			cntr->failed++;
			break;

		case TEST_WARNING:
			vzf_warning++;
			cntr->warning++;
			break;

		default:
			cntr->unknown++;
			break;
	}
}

