#include <includes.h>
#include "app_serv.h"
#include "modem.h"
#include "validator.h"
#include "coin.h"
#include "time.h"
#include "fiscal.h"
#include "menu.h"
#include "data.h"
#include "mode.h"
#include "menudesc.h"
#include "datadesc.h"
#include "control.h"
#include "validator.h"
#include "CCRSProtocol.h"
#include "menu.h"
#include "journal.h"
#include "fr.h"
#include "CRC16.h"
#include "modem_task.h"
#include "host_app.h"
#include "console.h"
#include "keyboard.h"
#include "term_tsk.h"
#include "fram_map.h"
#include "fram.h"
#include "sdcard.h"
#include "score_tsk.h"
#ifdef CONFIG_MUSIC_ENABLE
  #include "player.h"
#endif
#ifdef CONFIG_WEB_INTERFACE
  #include "httpd.h"
#endif
#ifdef CONFIG_MUSIC_ENABLE
  #include "music_tsk.h"
  extern int player_conn;
#endif
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#if defined(BOARD_SOLARIUM_VLAD)
  #include "app_vlad.h"
#endif
#include "rfid-spi.h"

#if defined(BOARD_SOLARIUM_WEB)
  ApplicationState app_state;
#endif

void PrintReports(void)
{
  if (!FlagForPrintReport) return;
  if (GetCurrentMenu() == CanselCheckMenuPanel)
  {
    int res = CanselFiscalBill();
    SaveEventRecord(0, JOURNAL_EVENT_PRINT_X, res);
    CheckFiscalStatus();
    GoToPreviousMenu();
    UserPrintResultMenu();
    GoToMenu(WORK_MENU);
  }
  else if (GetCurrentMenu() == xReportMenuPanel)
    { // печатаем X-отчет
     CPU_INT08U err;
     if (IsFiscalConnected())
      {
        FPend();
        FiscPrintDayReportNoClear(30, &err);
        FPost();
        if (err) {SetFiscalErrorByCode(err);}
        SaveEventRecord(0, JOURNAL_EVENT_PRINT_X, GetTimeSec());
        GoToPreviousMenu();
      }
    }
  else if (GetCurrentMenu() == zReportMenuPanel)
   { // печатаем Z-отчет
     CPU_INT08U err;
     if (IsFiscalConnected()) 
      {
        FPend();
        FiscPrintDayReportClear(30, &err);
        FPost();
        if (err) {SetFiscalErrorByCode(err);}
        SaveEventRecord(0, JOURNAL_EVENT_PRINT_Z, GetTimeSec());
        GoToPreviousMenu();
        ClrFiscalErrorByCode(FR_ERROR_CODE_4E);
      }
   }
  else if (GetCurrentMenu() == bufReportMenuPanel)
   { // печатаем Z-отчеты из буфера
     CPU_INT08U err;
     if (IsFiscalConnected()) 
      {
        FPend();
        FiscPrintDayReportsFromBuf(30, &err);
        FPost();
        if (err) {SetFiscalErrorByCode(err);}
        SaveEventRecord(0, JOURNAL_EVENT_PRINT_BUF, GetTimeSec());
        GoToPreviousMenu();
      }
   }
}

extern CPU_INT08U start_pass_ok;
// если определить этот макрос, будут вноситься деньги по кнопке F1
//#define _DEBUG_MONEY
uint32_t last_money, last_post;
extern TFramMap *config_ram;
CPU_INT32U SystemTime;
CPU_INT08U EnabledChannelsNum;
CPU_INT08U RecentChannel;
CPU_INT08U UserMenuState;
  #define USER_STATE_SELECT_POST                0
  #define USER_STATE_SELECT_PAY_TYPE            1
  #define USER_STATE_SELECT_MONEY               2
  #define USER_STATE_TERMINAL_REQUEST           3
  #define USER_STATE_ACCEPT_MONEY               4
  #define USER_STATE_SHOW_THANKS                5
  #define USER_STATE_FINISH_PAYMENT             6
CPU_INT08U ThanksCtr;
CPU_INT32U TerminalMoney;

CPU_INT08U ChannelsState[CHANNELS_NUM];
  #define CHANNEL_STATE_FREE            0
  #define CHANNEL_STATE_BUSY            1
  #define CHANNEL_STATE_DISABLED        2
CPU_INT32U ChannelsCounters[CHANNELS_NUM];
CPU_INT32U ChannelsPayedTime[CHANNELS_NUM];

extern CPU_INT32U BillNominals[24];
CPU_INT32U incas_bill_nom_counter[24];
CPU_INT32U incas_common_bill_counter;

#define USER_QUERY_LEN  64

static OS_STK    UserTaskStk[USER_TASK_STK_SIZE];
OS_EVENT *UserQuery = NULL;
void     *UserTbl[USER_QUERY_LEN];

#if defined(BOARD_SOLARIUM_WEB)
    CPU_INT32U http_last_status_time;
    char pass_edit = 0;
    char pass_phrase[10];
    #define USER_INPUT_STR_MAX_LEN  3
    #define MODE_PASSWORD   "*102#"
    
    CPU_INT08U zummer_enabled = 0;
    
    char message_lines[2][128];
    CPU_INT08U show_blocking_msg = 0;
        #define SHOW_BLOCKING_MSG_TIME  5

    void PrintSolariumBlockingMessage();
    void PrintServerDisconnectedMenu();
#endif

int GetUserEvent(int* event);
void UserPrintMoneyMenu(void);
void UserPrintPaymentErrorMenu(void);
void WorkServer(void);
void UserPrintThanksMenu(void);
void UserPrintSelectPostMenu(void);
void UserPrintErrorMenu(void);
CPU_INT32U GetChannelsTimeForFree(CPU_INT08U ch);
void LoadAcceptedMoney(void);
void SetAcceptedMoney(CPU_INT32U money);
void ClearAcceptedMoney(void);
CPU_INT32U GetAcceptedMoney(void);
void InitPass(void);
int CheckChannelEnabled(CPU_INT08U channel);
int ChannelBusy(CPU_INT08U ch);
void UserPrintIpDeviceErrMenu(CPU_INT08U post);
void UserPrintSelectMoneyMenu();
void UserPrintTerminalRequestMenu();
void UserPrintErrPayMenu();
void UserPrintSelectPayTypeMenu(void);
void PrintCardBill(void);
#if defined(BOARD_SOLARIUM_WEB)
void PrintSolariumFreeMenu();
void PrintSolariumPassMenu();
void PrintSolariumUserInputMenu();
void PrintWaitingAckUserMenu();
void PrintSolariumSunburnMenu();
void PrintSolariumPrePauseMenu();
void PrintSolariumVentilationMenu();
void SolariumStartUserInput();
CPU_INT32U SolariumGetUserInputTime();
CPU_INT32U SolariumFinishUserInput();
#endif
#if defined(BOARD_CENTRAL_CARWASH)
static char incassation;
static char was_critical_error;
static char terminal_pay_flag = 0;
#endif

static CPU_INT08U blink_counter;

#ifdef BOARD_POST_CARWASH

static int out_pulse_count = 0;
static int out_pulse_len = 0;
static int out_pulse_pause_len = 0;
int out_timer_counter = 0;
CPU_INT32U app_timer_value = 0;

void AddOutPulses(int count, int len_ms, int len_pause_ms)
{
    #if OS_CRITICAL_METHOD == 3
    OS_CPU_SR  cpu_sr = 0;
    #endif
    OS_ENTER_CRITICAL();
    
    out_pulse_count += count;
    out_pulse_len = len_ms;
    out_pulse_pause_len = len_pause_ms;
    
    OS_EXIT_CRITICAL();
}

#endif

int test_counter = 0;
int pre_pause_counter = 0;

///
CPU_INT32U TerminalEnabled()
{
    CPU_INT32U enabled = 0;
    GetData(&EnableTerminalDesc, &enabled, 0, DATA_FLAG_SYSTEM_INDEX);     
    return enabled;
}

void SelectFirstMenu()
{
  CPU_INT32U term_en = 0, validator_en = 0, coin_en = 0;
  GetData(&EnableTerminalDesc, &term_en, 0, DATA_FLAG_SYSTEM_INDEX);
  GetData(&EnableValidatorDesc, &validator_en, 0, DATA_FLAG_SYSTEM_INDEX);  
  GetData(&EnableCoinDesc, &coin_en, 0, DATA_FLAG_SYSTEM_INDEX);  
  if (term_en)
  {
      if (validator_en || coin_en)
      {
          UserMenuState = USER_STATE_SELECT_PAY_TYPE;
          UserPrintSelectPayTypeMenu();
          RefreshMenu();
      }
      else
      {
          UserMenuState = USER_STATE_SELECT_MONEY;
          UserPrintSelectMoneyMenu();
          RefreshMenu();
      }
  }
  else
  {
      UserMenuState = USER_STATE_ACCEPT_MONEY;
      if (IsValidatorConnected()) CC_CmdBillType(0xffffff, 0xffffffff, ADDR_FL);
      CoinEnable();
      UserPrintMoneyMenu();
      RefreshMenu();
  }

}

#if defined(BOARD_SOLARIUM_WEB)
const char* AppStatusStr(CPU_INT08U state)
{
    static const char state_idle[] = "idle";
    static const char state_waiting_input[] = "waiting_input";
    static const char state_waiting_ack[] = "waiting_ack";
    static const char state_pre_pause[] = "pre_pause";
    static const char state_sunburn[] = "sunburn";
    static const char state_ventilation[] = "ventilation";
    static const char state_unknown[] = "unknown";

    if (state == APP_STATE_IDLE) return state_idle;
    else if (state == APP_STATE_USER_INPUT) return state_waiting_input;
    else if (state == APP_STATE_WAITING_ACK) return state_waiting_ack;
    else if (state == APP_STATE_PRE_PAUSE) return state_pre_pause;
    else if (state == APP_STATE_SUNBURN) return state_sunburn;
    else if (state == APP_STATE_VENTILATION) return state_ventilation;
    
    return state_unknown;
}

/// расчет времени загара в секундах по сумме денег
CPU_INT32U CalcTimeSecPerMoney(CPU_INT32U money)
{
    CPU_INT32U price;
    GetData(&SolariumPricePerMinDesc, &price, 0, DATA_FLAG_SYSTEM_INDEX);
    if (price == 0) return 0;
    return (CPU_INT32U)((float)money / (float)price * 60.0);
}

uint32_t zummer_timer = 0;

void ZummerBeep(void)
{
    zummer_timer = 100;
}

void ZummerLongBeep(void)
{
    zummer_timer = 400;
}

char user_input_str[8];
CPU_INT08U user_input_ctr;
static const CPU_INT08U user_input_pos[4] = {0, 1, 3, 4};


#endif


void          App_TaskCreateHook    (OS_TCB          *ptcb) {}
void          App_TaskDelHook       (OS_TCB          *ptcb) {}
void          App_TaskIdleHook      (void) {}
void          App_TaskStatHook      (void) {}
#if OS_TASK_SW_HOOK_EN > 0
void          App_TaskSwHook        (void) {}
#endif
void          App_TCBInitHook       (OS_TCB          *ptcb) {}

extern void app_1ms_callback(void);

void App_TimeTickHook()
{
    #if defined(BOARD_SOLARIUM_WEB)
    if (zummer_enabled)
    {
        if (zummer_timer) {zummer_timer--; SOLARIUM_ZUMMER_ON();}
        else {SOLARIUM_ZUMMER_OFF();}
    }
    else
    {
        SOLARIUM_ZUMMER_OFF();
        zummer_timer = 0;
    }
    #endif
    #if defined(BOARD_SOLARIUM_VLAD)
    app_1ms_callback();
    #endif
}

/*!
 Сервер обработки событий пользователя
*/
void UserAppTask(void *p_arg)
{
  int event;
  
  UserStartupFunc();
      
#if defined(BOARD_CENTRAL_CARWASH)
  CPU_INT32U temp;
  
  static CPU_INT08U fr_conn_ctr = 0;

  SelectFirstMenu();

  {
    CPU_INT32U m=0;
    GetData(&AcceptedMoneyDesc, &m, 0, DATA_FLAG_SYSTEM_INDEX);     
    if (m)
    {
         EnabledChannelsNum = 0;
         for (CPU_INT08U i=0; i<CHANNELS_NUM; i++)
         {
            CPU_INT32U en = 0;
            GetData(&EnableChannelDesc, &en, i, DATA_FLAG_DIRECT_INDEX);
            if (en) {EnabledChannelsNum++;}
         }
         UserMenuState = USER_STATE_ACCEPT_MONEY;
    }
  }
  
  incassation = 0;
  was_critical_error = 0;
  
  GetData(&IncasSendFlagDesc, &temp, 0, DATA_FLAG_SYSTEM_INDEX);
  if (temp == INCAS_SEND_FLAG)
  {
      PostModemTask(MODEM_TASK_SEND_INCAS);
  }
#elif defined(BOARD_SOLARIUM_WEB)
    app_state.state = APP_STATE_IDLE;
    app_state.userinputtime = -1;
    app_state.userinputmoney = -1;
    SetMenu(WORK_MENU);
#elif defined(BOARD_SOLARIUM_VLAD)
    AppInitVlad();
#endif
  
  while (1)
    {
      if (GetUserEvent(&event))
        {
#if defined(BOARD_SOLARIUM_VLAD)
          if ((event == EVENT_KEY_START) && 
               ((GetCurrentMenu() == xReportMenuPanel) || (GetCurrentMenu() == zReportMenuPanel)
                || (GetCurrentMenu() == bufReportMenuPanel)
                || (GetCurrentMenu() == CanselCheckMenuPanel)
               )
             )
          {
            PrintReports();
          }
          else
          {
            AppVladEventProcess(event);
          }
#else
          switch (event){
#endif

#if defined(BOARD_SOLARIUM_WEB)
            case EVENT_SYSTEM_REBOOT:
                OSTimeDly(5000);
                Reset();
                break;
            case EVENT_REQUEST_USER_INPUT:
                if ((GetMode() == MODE_WORK) && (app_state.state == APP_STATE_IDLE))
                {
                    app_state.userinputtime = -1;
                    app_state.userinputmoney = -1;
                    app_state.state = APP_STATE_USER_INPUT;
                    GetData(&SolariumTimeoutDesc, &app_state.timetoend, 0, DATA_FLAG_SYSTEM_INDEX);
                    SolariumStartUserInput();
                    PrintSolariumUserInputMenu();
                }
                break;
            case EVENT_USER_INPUT_FINISHED:
                app_state.userinputtime = SolariumFinishUserInput();
                if ((app_state.state == APP_STATE_USER_INPUT) && (app_state.userinputtime > 0) &&
                    ((app_state.timefromserver > 0)
                     || ((app_state.moneyfromserver > 0) && (CalcTimeSecPerMoney(app_state.moneyfromserver) >= app_state.userinputtime))
                     )
                   )
                {
                    if (app_state.moneyfromserver > 0)
                    {
                        CPU_INT32U price;
                        GetData(&SolariumPricePerMinDesc, &price, 0, DATA_FLAG_SYSTEM_INDEX);
                        app_state.userinputmoney = (CPU_INT32U)((float)(app_state.userinputtime * price) / 60.0);
                    }
                    app_state.state = APP_STATE_WAITING_ACK;
                    GetData(&SolariumTimeoutDesc, &app_state.timetoend, 0, DATA_FLAG_SYSTEM_INDEX);
                    PrintWaitingAckUserMenu();
                }
                else
                {
                    app_state.state = APP_STATE_IDLE;
                    PrintSolariumFreeMenu();
                }
                break;
            case EVENT_ACK_USER_INPUT:
                if (app_state.state == APP_STATE_WAITING_ACK)
                {
                    if ((app_state.userinputtime > 0) && (app_state.timefromserver > 0) || (app_state.moneyfromserver > 0))
                    {
                        CPU_INT32U pre_pause = 0;
                        GetData(&SolariumPauseDesc, &pre_pause, 0, DATA_FLAG_SYSTEM_INDEX);
                        if (pre_pause > 0)
                        {
                            app_state.state = APP_STATE_PRE_PAUSE;
                            app_state.timetoend = pre_pause;
                        }
                        else
                        {
                            if (app_state.timefromserver > 0)
                            {
                                app_state.timetoend = app_state.userinputtime;
                            }
                            else
                            {
                                app_state.timetoend = CalcTimeSecPerMoney(app_state.moneyfromserver);
                            }
                            app_state.state = APP_STATE_SUNBURN;
                        }
                    }
                    else
                    {
                        app_state.state = APP_STATE_IDLE;
                    }
                }
                break;
            case EVENT_NACK_USER_INPUT:
                if (app_state.state != APP_STATE_IDLE)
                {
                    MenuCursorOff();
                    app_state.state = APP_STATE_IDLE;
                    app_state.timetoend = 0;
                    app_state.userinputtime = -1;
                    app_state.userinputmoney = -1;
                    show_blocking_msg = SHOW_BLOCKING_MSG_TIME;
                    PrintSolariumBlockingMessage();
                }
                break;
            case EVENT_SHOW_ZERO_MONEY:
            case EVENT_ABORT_OPERATIONS:
                if (app_state.state != APP_STATE_IDLE)
                {
                    MenuCursorOff();
                    SOLARIUM_LAMPS_OFF();
                    SOLARIUM_VENT_OFF();
                    app_state.state = APP_STATE_IDLE;
                    app_state.timetoend = 0;
                    app_state.userinputtime = -1;
                    app_state.userinputmoney = -1;
                    PrintSolariumFreeMenu();
                }
                break;
            case EVENT_SEC:
                // прочитаем текущее время
                SystemTime = GetTimeSec();
                
                if (show_blocking_msg)
                {
                    show_blocking_msg--;
                    PrintSolariumBlockingMessage();
                }

                if (!pass_edit) 
                {
                    if (app_state.state == APP_STATE_IDLE)
                    {
                        PrintSolariumFreeMenu();
                        SOLARIUM_LAMPS_OFF();
                        SOLARIUM_VENT_OFF();
                        if (SystemTime - http_last_status_time > HTTP_SERVER_CONN_OK_TIMEOUT)
                        {
                            PrintServerDisconnectedMenu();
                        }
                    }
                    else if (app_state.state == APP_STATE_USER_INPUT)
                    {
                        if (app_state.timetoend > 0)
                        {
                            PrintSolariumUserInputMenu();
                            app_state.timetoend--;
                        }
                        else
                        {
                            app_state.state = APP_STATE_IDLE;
                            MenuCursorOff();
                            PrintSolariumFreeMenu();
                        }
                    }
                    else if (app_state.state == APP_STATE_WAITING_ACK)
                    {
                        if (app_state.timetoend > 0)
                        {
                            PrintWaitingAckUserMenu();
                            app_state.timetoend--;
                        }
                        else
                        {
                            app_state.state = APP_STATE_IDLE;
                            PrintSolariumFreeMenu();
                            SetMenu(WORK_MENU);
                        }
                    }
                    else if (app_state.state == APP_STATE_PRE_PAUSE)
                    {
                        if (app_state.timetoend)
                        {
                            PrintSolariumPrePauseMenu();
                            SOLARIUM_LAMPS_OFF();
                            SOLARIUM_VENT_OFF();
                            app_state.timetoend--;
                        }
                        else
                        {
                            if (app_state.timefromserver > 0)
                            {
                                app_state.state = APP_STATE_SUNBURN;
                                app_state.timetoend = app_state.userinputtime;
                                IncCounter(0, app_state.userinputtime, 0, 0);
                                SaveEventRecord(0, JOURNAL_EVENT_START_SESSION, app_state.userinputtime);
                                //PrintSolariumSunburnMenu();
                            }
                            else if (app_state.moneyfromserver > 0)
                            {
                                app_state.state = APP_STATE_SUNBURN;
                                app_state.timetoend = app_state.userinputtime;
                                IncCounter(0, app_state.userinputtime, 0, 0);
                                SaveEventRecord(0, JOURNAL_EVENT_START_SESSION, app_state.userinputtime);
                                //PrintSolariumSunburnMenu();
                            }
                            else
                            {
                                app_state.state = APP_STATE_IDLE;
                                PrintSolariumFreeMenu();
                                SetMenu(WORK_MENU);
                            }
                        }
                    }
                    else if (app_state.state == APP_STATE_SUNBURN)
                    {
                        PrintSolariumSunburnMenu();
                        if (app_state.timetoend)
                        {
                            CPU_INT32U sol_type = 0;
                            GetData(&SolariumTypeDesc, &sol_type, 0, DATA_FLAG_SYSTEM_INDEX);
                            if (sol_type == 0)
                            {
                                SOLARIUM_LAMPS_ON();
                                SOLARIUM_VENT_OFF();
                            }
                            else
                            {
                                SOLARIUM_LAMPS_ON();
                                SOLARIUM_VENT_ON();
                            }
                            app_state.timetoend--;
                        }
                        else
                        {
                            CPU_INT32U vent_time = 0;
                            GetData(&SolariumVentilationTimeDesc, &vent_time, 0, DATA_FLAG_SYSTEM_INDEX);
                            if (vent_time > 0)
                            {
                                CPU_INT32U sol_type = 0;
                                GetData(&SolariumTypeDesc, &sol_type, 0, DATA_FLAG_SYSTEM_INDEX);
                                if (sol_type == 0)
                                {
                                    SOLARIUM_LAMPS_ON();
                                    SOLARIUM_VENT_OFF();
                                }
                                else
                                {
                                    SOLARIUM_LAMPS_OFF();
                                    SOLARIUM_VENT_ON();
                                }
                                app_state.state = APP_STATE_VENTILATION;
                                app_state.timetoend = vent_time;
                            }
                            else
                            {
                                SOLARIUM_VENT_OFF();
                                SOLARIUM_LAMPS_OFF();
                                app_state.state = APP_STATE_IDLE;
                            }
                            
                            SaveEventRecord(0, JOURNAL_EVENT_END_SESSION, app_state.userinputtime);
                        }
                    }
                    else if (app_state.state == APP_STATE_VENTILATION)
                    {
                        PrintSolariumVentilationMenu();
                        if (app_state.timetoend)
                        {
                            app_state.timetoend--;
                            CPU_INT32U sol_type = 0;
                            GetData(&SolariumTypeDesc, &sol_type, 0, DATA_FLAG_SYSTEM_INDEX);
                            if (sol_type == 0)
                            {
                                SOLARIUM_LAMPS_ON();
                                SOLARIUM_VENT_OFF();
                            }
                            else
                            {
                                SOLARIUM_LAMPS_OFF();
                                SOLARIUM_VENT_ON();
                            }
                        }
                        else
                        {
                            SOLARIUM_LAMPS_OFF();
                            SOLARIUM_VENT_OFF();
                            app_state.state = APP_STATE_IDLE;
                        }
                    }
                }
                else
                {
                    PrintSolariumPassMenu();
                }
          
                break;
            case EVENT_KEY_STAR:
                
                if (show_blocking_msg)
                {
                    PrintSolariumBlockingMessage();
                    break;
                }

                if (app_state.state == APP_STATE_USER_INPUT)
                {
                    app_state.state = APP_STATE_IDLE;
                    MenuCursorOff();
                    PrintSolariumFreeMenu();
                }
                else if ((GetMode() == MODE_WORK) && (app_state.state == APP_STATE_IDLE))
                {
                    if (!pass_edit)
                    {
                        pass_edit = 1;
                        memset(pass_phrase, 0, sizeof(pass_phrase));
                        pass_phrase[0] = '*';
                        PrintSolariumPassMenu();
                    }
                    else
                    {
                        pass_edit = 0;
                        PrintSolariumFreeMenu();
                    }
                }
                break;
            case EVENT_KEY_SHARP:
                
                if (show_blocking_msg)
                {
                    PrintSolariumBlockingMessage();
                    break;
                }

                if (app_state.state == APP_STATE_USER_INPUT)
                {
                    CPU_INT32U mintime, maxtime;
                    CPU_INT32U usertime = SolariumGetUserInputTime();

                    GetData(&SolariumMinTimeDesc, &mintime, 0, DATA_FLAG_SYSTEM_INDEX);    
                    GetData(&SolariumMaxTimeDesc, &maxtime, 0, DATA_FLAG_SYSTEM_INDEX);    

                    if (usertime > maxtime * 60)
                    {
                        strcpy(message_lines[0], "МАКСИМАЛ.ВРЕМЯ");
                        sprintf(message_lines[1], "ЗАГАРА %d МИНУТ", maxtime);
                        show_blocking_msg = SHOW_BLOCKING_MSG_TIME;
                        PrintSolariumBlockingMessage();
                        GetData(&SolariumTimeoutDesc, &app_state.timetoend, 0, DATA_FLAG_SYSTEM_INDEX);
                        SolariumStartUserInput();
                        MenuCursorOff();
                    }
                    else if (usertime < mintime * 60)
                    {
                        strcpy(message_lines[0], "МИНИМАЛЬН.ВРЕМЯ");
                        sprintf(message_lines[1], "ЗАГАРА %d МИНУТ", mintime);
                        show_blocking_msg = SHOW_BLOCKING_MSG_TIME;
                        PrintSolariumBlockingMessage();
                        GetData(&SolariumTimeoutDesc, &app_state.timetoend, 0, DATA_FLAG_SYSTEM_INDEX);
                        SolariumStartUserInput();
                        MenuCursorOff();
                    }
                    else
                    {
                        PostUserEvent(EVENT_USER_INPUT_FINISHED);
                    }
                }
                else if ((GetMode() == MODE_WORK) && (app_state.state == APP_STATE_IDLE))
                {
                    if (pass_edit)
                    {
                        if (strlen(pass_phrase) < 9)
                        {
                            pass_phrase[strlen(pass_phrase)] = '#';
                            PrintSolariumPassMenu();
                            OSTimeDly(500);
                            if (strcmp(pass_phrase, MODE_PASSWORD) == 0)
                            {
                                memset(pass_phrase, 0, sizeof(pass_phrase));
                                SetMode(MODE_SERVICE);
                                GoToMenu(SERVICE_MENU);
                            }
                            else
                            {
                                PrintSolariumPassMenu();
                            }
                        }
                        else
                        {
                            pass_edit = 0;
                            PrintSolariumFreeMenu();
                        }
                    }
                }
                break;
            case EVENT_KEY_DIGIT0:
            case EVENT_KEY_DIGIT1:
            case EVENT_KEY_DIGIT2:
            case EVENT_KEY_DIGIT3:
            case EVENT_KEY_DIGIT4:
            case EVENT_KEY_DIGIT5:
            case EVENT_KEY_DIGIT6:
            case EVENT_KEY_DIGIT7:
            case EVENT_KEY_DIGIT8:
            case EVENT_KEY_DIGIT9:
                
                if (show_blocking_msg)
                {
                    PrintSolariumBlockingMessage();
                    break;
                }

                if (app_state.state == APP_STATE_USER_INPUT)
                {
                    user_input_str[user_input_pos[user_input_ctr]] = '0' + event - EVENT_KEY_DIGIT0;
                    user_input_ctr++;
                    user_input_ctr %= 4;
                    PrintSolariumUserInputMenu();
                }   
                else if ((GetMode() == MODE_WORK) && (app_state.state == APP_STATE_IDLE))
                {
                    if (pass_edit)
                    {
                        if (strlen(pass_phrase) < 9)
                        {
                            pass_phrase[strlen(pass_phrase)] = '0' + event - EVENT_KEY_DIGIT0;
                            PrintSolariumPassMenu();
                        }
                        else
                        {
                            pass_edit = 0;
                            PrintSolariumFreeMenu();
                        }
                    }
                }
                break;
         

#elif defined(BOARD_CENTRAL_CARWASH)
            case EVENT_SEC:
              // проверка режима
              CheckMode();
             
              // прочитаем текущее время
              SystemTime = GetTimeSec();
                
              // рабочий сервер - счетчики, состояния и т.п.
              if (GetMode() == MODE_WORK)
              {
                  WorkServer();
              }
              
              #if defined(BOARD_CENTRAL_CARWASH)
              // проверим фискальник, если он отвалился
              if ((++fr_conn_ctr % 10) == 0)
              {
                 if ((FiscalConnState == FISCAL_NOCONN) || (TstCriticalFiscalError()))
                 {
                    if (ConnectFiscalFast() == 0)
                    {
                        CheckFiscalStatus();
                    }
                 }
              }
              #endif
              
              // сервер ошибок
              ErrorServer();
                
              // дальше только в рабочем режиме
              if (GetMode() != MODE_WORK)
              {
                  SelAllLeds(1);
                  break;
              }
                
              #if defined(BOARD_CENTRAL_CARWASH)
              #if !APP_DEBUG
              // если есть ошибки, не работаем
              if (TstCriticalErrors()) 
              {
                UserPrintErrorMenu(); 
                RefreshMenu(); 
                if (UserMenuState == USER_STATE_ACCEPT_MONEY)
                {
                    // выключим прием денег, если в меню приема денег
                    if (was_critical_error == 0)
                    {
                        if (IsValidatorConnected()) CC_CmdBillType(0x000000, 0x000000, ADDR_FL);
                        CoinDisable();
                        was_critical_error = 1;
                    }
                }
                else
                {
                    was_critical_error = 0;
                }
                break;
              }
              #endif
              #endif
              
              #if defined(BOARD_CENTRAL_CARWASH)
              if (UserMenuState == USER_STATE_SELECT_POST)
              { 
                  // обновляем первую страницу
                  CoinDisable();
                  UserPrintSelectPostMenu();
                  RefreshMenu(); 
                  if (++blink_counter & 0x1)
                  {
                      SetBacklight(LED_OK, 1);
                      SetBacklight(LED_CANSEL, 0);
                  }
                  else
                  {
                      SetBacklight(LED_OK, 0);
                      SetBacklight(LED_CANSEL, 1);
                  }
              }
              else if (UserMenuState == USER_STATE_ACCEPT_MONEY)
              { 
                  // принимаем деньги
                  UserPrintMoneyMenu();
                  RefreshMenu();
                  CheckFiscalStatus();
                  // включим заново прием денег, если была ошибка в этом же меню
                  if (was_critical_error)
                  {
                      if (IsValidatorConnected()) CC_CmdBillType(0xffffff, 0xffffff, ADDR_FL);
                      CoinEnable();
                      was_critical_error = 0;
                  }
              }
              else if (UserMenuState == USER_STATE_SHOW_THANKS)
              {
                  // показываем "СПАСИБО"
                  CoinDisable();
                  if (ThanksCtr)
                  {
                      UserPrintThanksMenu();
                      RefreshMenu();
                      ThanksCtr--;
                  }
                  else 
                  {
                      SelectFirstMenu();
                  }
              }
              else if (UserMenuState == USER_STATE_SELECT_PAY_TYPE)
              {
                  UserPrintSelectPayTypeMenu();
                  RefreshMenu();
              }
              else if (UserMenuState == USER_STATE_SELECT_MONEY)
              {
                  UserPrintSelectMoneyMenu();
                  RefreshMenu();
              }
              else if (UserMenuState == USER_STATE_TERMINAL_REQUEST)
              {
                  if (GetTermState() == TERM_STATE_DONE_PUR)
                  {
                      /// печать чека терминала
                      SetTermCommand(TERM_COMMAND_CLEAR_STATE, NULL);
                      TerminalMoney += GetAcceptedMoney();
                      SetAcceptedMoney(TerminalMoney);
                      SaveEventRecord(RecentChannel, JOURNAL_EVENT_CARD_ACCEPTED, TerminalMoney);
                      if (term_protocol() == TERMINAL_PROTOCOL_TTK2) PrintCardBill();
                      PostUserEvent(EVENT_KEY_START);
                      terminal_pay_flag = 1;
                  }
                  else if (GetTermState() == TERM_STATE_ERR_PUR)
                  {
                      SetTermCommand(TERM_COMMAND_CLEAR_STATE, NULL);
                      TerminalMoney = 0;
                      SaveEventRecord(RecentChannel, JOURNAL_EVENT_CARD_REJECTED, 0);
                      UserPrintPaymentErrorMenu();
                      RefreshMenu();
                      OSTimeDly(2000);
                      PostUserEvent(EVENT_KEY_CANSEL);
                  }
                  else
                  {
                      UserPrintTerminalRequestMenu();
                      RefreshMenu();
                  }
              }
              #endif
              break;
            #if defined(BOARD_CENTRAL_CARWASH)
            case EVENT_INCASSATION:
              {
                  CPU_INT32U incas_sum = 0, temp;              
                  for (CPU_INT32U i = 0; i < 24; i++)
                  {
                      CPU_INT32U val = 0;
                      GetData(&BillnomCountersDesc, &val, i, DATA_FLAG_DIRECT_INDEX);
                      incas_sum += val*BillNominals[i];
                  }              
                  incassation = 1;
                  sprintf((char*)str_UserMenu_3, "  СУММА %u руб.", incas_sum);
                  // вешаем меню инкассация
                  GoToMenu(IncasMenuPanel);
                  // сохраним событие с указанием суммы денег
                  SaveEventRecord(0, JOURNAL_EVENT_INCASSATION, incas_sum);
                  GetData(&BillCounterDesc, &incas_common_bill_counter, 0, DATA_FLAG_SYSTEM_INDEX);
                  for (CPU_INT32U i = 0; i < 24; i++)
                  {
                     GetData(&BillnomCountersDesc, &incas_bill_nom_counter[i], i, DATA_FLAG_DIRECT_INDEX);
                  }
                  
                  SetData(&IncasMoneyDesc, &incas_sum, 0, DATA_FLAG_SYSTEM_INDEX);
                  
                  temp = GetTimeSec();
                  SetData(&IncasTimeDesc, &temp, 0, DATA_FLAG_SYSTEM_INDEX);
                  
                  temp = INCAS_SEND_FLAG;
                  SetData(&IncasSendFlagDesc, &temp, 0, DATA_FLAG_SYSTEM_INDEX);
                  PostModemTask(MODEM_TASK_SEND_INCAS);
                  // очищаем счетчики купюр
                  ClearBillnomCounter();
              }
              break;
            case EVENT_INCASSATION_FINISH:
              incassation = 0;
              GoToPreviousMenu();
              SelectFirstMenu();
              break;
            #endif
            case EVENT_MODE_CHANGE:
              ReInitMenu();
              SaveEventRecord(0, JOURNAL_EVENT_CHANGE_MODE, GetMode());
              if (GetMode() == MODE_SERVICE)
              {
                SelAllLeds(1);
              }
              else
              {
                SelAllLeds(0);
              }
              break;
            case EVENT_COIN_INSERTED:
              {
                CPU_INT32U cpp = 1;
                CPU_INT32U money, accmoney;
                GetData(&CoinPerPulseDesc, &cpp, 0, DATA_FLAG_SYSTEM_INDEX);
                money = cpp*GetResetCoinCount();
                accmoney = GetAcceptedMoney();
                accmoney += money;
                SetAcceptedMoney(accmoney);
                if (UserMenuState == USER_STATE_ACCEPT_MONEY)
                {
                    UserPrintMoneyMenu();  
                    RefreshMenu();
                }
                SaveEventRecord(RecentChannel, JOURNAL_EVENT_MONEY_COIN, money);
              }
              break;
            case EVENT_BILL_ESCROW:
                // купюра в положении возврата
                if (UserMenuState != USER_STATE_ACCEPT_MONEY)
                {
                    if (IsValidatorConnected()) if (!CC_CmdReturn(ADDR_FL)) SetErrorFlag(ERROR_VALIDATOR_CONN);
                }
                else
                {
                    if (IsValidatorConnected()) if (!CC_CmdPack(ADDR_FL)) SetErrorFlag(ERROR_VALIDATOR_CONN);
                }
                break;
            case EVENT_BILL_STACKED:
              // купюра уложена
              {
                CPU_INT32U billnom_index;
                CPU_INT32U note,accmoney;
                note = GetResetBillCount(&billnom_index);
                accmoney = GetAcceptedMoney();
                accmoney += note;
                SetAcceptedMoney(accmoney);
                if (UserMenuState == USER_STATE_ACCEPT_MONEY)
                {
                    UserPrintMoneyMenu();  
                    RefreshMenu();
                }
                if (IsValidatorConnected()) CC_CmdBillType(0xffffff, 0xffffffff, ADDR_FL);
                CoinEnable();
                SaveEventRecord(RecentChannel, JOURNAL_EVENT_MONEY_NOTE, note);
                IncBillnomCounter(billnom_index);
              }
              break;                  
          
            #if defined(BOARD_CENTRAL_CARWASH)
            case EVENT_KEY_POST1:
            case EVENT_KEY_POST2:
            case EVENT_KEY_POST3:
            case EVENT_KEY_POST4:
            case EVENT_KEY_POST5:
            case EVENT_KEY_POST6:
            case EVENT_KEY_POST7:
            case EVENT_KEY_POST8:
              if ((GetMode() != MODE_WORK) || (incassation)) break;
              #if !APP_DEBUG
              if (TstCriticalErrors())
              {
                  UserPrintErrorMenu(); 
                  RefreshMenu(); 
                  break;
              }
              #endif
              
              #if APP_DEBUG
              if ((event == EVENT_KEY_POST5) && (UserMenuState == USER_STATE_ACCEPT_MONEY))
              {
                  CPU_INT32U note,accmoney;
                  note = 100;
                  accmoney = GetAcceptedMoney();
                  accmoney += note;
                  SetAcceptedMoney(accmoney);
              }   
              #endif

              if (UserMenuState == USER_STATE_SELECT_MONEY)
              {
                  GetData(&SelectedMoneyDesc, &TerminalMoney, event - EVENT_KEY_POST1, DATA_FLAG_DIRECT_INDEX);   
                  UserMenuState = USER_STATE_TERMINAL_REQUEST;
                  uint32_t param[TERM_PARAM_COUNT];
                  param[0] = TerminalMoney;
                  SetTermCommand(TERM_COMMAND_PEND_PUR, param);
                  UserPrintTerminalRequestMenu();
                  RefreshMenu();
                  break;
              }
              
              if (CheckChannelEnabled(event - EVENT_KEY_POST1)
                   && !ChannelBusy(event - EVENT_KEY_POST1))
              {
                  if ((UserMenuState == USER_STATE_SELECT_POST)
                      || ((UserMenuState == USER_STATE_ACCEPT_MONEY) && (GetAcceptedMoney() > 0)))
                  {
                      #if !APP_DEBUG
                      CPU_INT32U ip;
                      GetData(&PostIpAddrDesc, &ip, event - EVENT_KEY_POST1, DATA_FLAG_DIRECT_INDEX);   
                      if (HostCheckIpDevice(ip, CONSOLE_TCP_DEFAULT_PORT, HOST_SOCKET_DEFAULT_TIMEOUT) != 0)
                      {
                        SaveEventRecord(event - EVENT_KEY_POST1, JOURNAL_EVENT_POST_UNAVIABLE, 0);
                        UserPrintIpDeviceErrMenu(event - EVENT_KEY_POST1);
                        RefreshMenu();
                        OSTimeDly(1000);
                        break;
                      }
                      else
                      #endif
                      {
                        RecentChannel = event - EVENT_KEY_POST1;
                        PostUserEvent(EVENT_KEY_START);
                        UserMenuState = USER_STATE_FINISH_PAYMENT;
                      }
                  }

                  break;
              }
              
              break;
            #endif
              
            case EVENT_KEY_CANSEL:
              if ((GetMode() != MODE_WORK)
                  #if defined(BOARD_CENTRAL_CARWASH)
                  || (incassation)
                  #endif
                      ) break;
              #if !APP_DEBUG
              if (TstCriticalErrors())
              {
                  UserPrintErrorMenu(); 
                  RefreshMenu(); 
                  break;
              }
              #endif

              if (UserMenuState == USER_STATE_SELECT_PAY_TYPE)
              {
                  #ifdef BOARD_SWAP_OK_CANSEL
                  UserMenuState = USER_STATE_ACCEPT_MONEY;
                  UserPrintMoneyMenu();
                  RefreshMenu();
                  if (IsValidatorConnected()) CC_CmdBillType(0xffffff, 0xffffffff, ADDR_FL);
                  CoinEnable();
                  #else
                  UserMenuState = USER_STATE_SELECT_MONEY;
                  UserPrintSelectMoneyMenu();
                  RefreshMenu();
                  #endif
                  break;
              }
              
              if (UserMenuState == USER_STATE_TERMINAL_REQUEST)
              {
                  SetTermCommand(TERM_COMMAND_ABORT_PUR, NULL);
                  SelectFirstMenu();
                  break;
              }
              
              if (UserMenuState == USER_STATE_ACCEPT_MONEY)
              {
                  CPU_INT32U term_en = 0, validator_en = 0, coin_en = 0;
                  GetData(&EnableTerminalDesc, &term_en, 0, DATA_FLAG_SYSTEM_INDEX);
                  GetData(&EnableValidatorDesc, &validator_en, 0, DATA_FLAG_SYSTEM_INDEX);  
                  GetData(&EnableCoinDesc, &coin_en, 0, DATA_FLAG_SYSTEM_INDEX);
                  if (term_en)
                  {
                      if (IsValidatorConnected()) CC_CmdBillType(0x000000, 0x000000, ADDR_FL);
                      CoinDisable();
                      SelectFirstMenu();
                  }
                  break;
              }
                 
              break;
              
            case EVENT_KEY_START:
              #if defined(BOARD_CENTRAL_CARWASH)
              if (incassation) break;
              #endif
              if (GetMode() != MODE_WORK)
                {
                  PrintReports();
                  break;
                }
            
              #if !APP_DEBUG
              if (TstCriticalErrors()) 
              {
                UserPrintErrorMenu(); 
                RefreshMenu(); 
                break;
              }
              #endif
              
              if (UserMenuState == USER_STATE_SELECT_PAY_TYPE)
              {
                  #ifdef BOARD_SWAP_OK_CANSEL
                  UserMenuState = USER_STATE_SELECT_MONEY;
                  UserPrintSelectMoneyMenu();
                  RefreshMenu();
                  #else       
                  UserMenuState = USER_STATE_ACCEPT_MONEY;
                  UserPrintMoneyMenu();
                  RefreshMenu();
                  if (IsValidatorConnected()) CC_CmdBillType(0xffffff, 0xffffffff, ADDR_FL);
                  CoinEnable();
                  #endif                  
                  break;
              }

              if (UserMenuState == USER_STATE_TERMINAL_REQUEST)
              {
                  if (GetAcceptedMoney() > 0)
                  {
                      UserMenuState = USER_STATE_SELECT_POST;
                      UserPrintSelectPostMenu();
                      RefreshMenu(); 
                      CoinDisable();
                      break;
                  }
              }
                  
              // --------------------------
              // находимся в рабочем режиме
              // --------------------------
              #if defined(BOARD_CENTRAL_CARWASH)
              if (UserMenuState == USER_STATE_FINISH_PAYMENT)
                {
                  // пользователь внес деньги и нажал СТАРТ
                  CPU_INT32U price = 1, accmoney;
                  CPU_INT32U post_timer;
 
                  GetData(&PostMinutePriceDesc, &price, RecentChannel, DATA_FLAG_DIRECT_INDEX);     
                  accmoney = GetAcceptedMoney();
                  
                  GetData(&PostTimerDesc, &post_timer, RecentChannel, DATA_FLAG_DIRECT_INDEX);
                  if (post_timer == 0)
                  {
                      ChannelsPayedTime[RecentChannel] = (accmoney * 60) / price;
                  }
                  else
                  {
                      ChannelsPayedTime[RecentChannel] = post_timer * accmoney;
                  }
                  
                  if (ChannelsPayedTime[RecentChannel] > 0)
                    { 
                      // отправим команду на исполнительное устройство
                      CPU_INT32U ip;
                      CPU_INT32U len_ms,len_pause_ms, imp_cost, imp_pause;
                      
                      GetData(&PostIpAddrDesc, &ip, RecentChannel, DATA_FLAG_DIRECT_INDEX);   
                      GetData(&PostImpCostDesc, &imp_cost, RecentChannel, DATA_FLAG_DIRECT_INDEX);   
                      GetData(&PostLenCostDesc, &len_ms, RecentChannel, DATA_FLAG_DIRECT_INDEX);
                      GetData(&PostPauseLenCostDesc, &len_pause_ms, RecentChannel, DATA_FLAG_DIRECT_INDEX);
                      GetData(&PostPauseDesc, &imp_pause, RecentChannel, DATA_FLAG_DIRECT_INDEX);
                      GetData(&PostTimerDesc, &post_timer, RecentChannel, DATA_FLAG_DIRECT_INDEX);
                      #if !APP_DEBUG
                      if (post_timer == 0)
                      {
                          if (HostWritePulses(ip, accmoney / imp_cost, len_ms, len_pause_ms, imp_pause, post_timer) != 0)
                          {
                            SaveEventRecord(RecentChannel, JOURNAL_EVENT_POST_UNAVIABLE, 0);
                            UserPrintIpDeviceErrMenu(RecentChannel);
                            RefreshMenu();
                            OSTimeDly(1000);
                            if (terminal_pay_flag) UserMenuState = USER_STATE_SELECT_POST;
                            else UserMenuState = USER_STATE_ACCEPT_MONEY;
                            break;
                          }
                      }
                      else 
                      {
                          if (HostWritePulses(ip, accmoney, 0, 0, imp_pause, post_timer) != 0)
                          {
                            SaveEventRecord(RecentChannel, JOURNAL_EVENT_POST_UNAVIABLE, 0);
                            UserPrintIpDeviceErrMenu(RecentChannel);
                            RefreshMenu();
                            OSTimeDly(1000);
                            if (terminal_pay_flag) UserMenuState = USER_STATE_SELECT_POST;
                            else UserMenuState = USER_STATE_ACCEPT_MONEY;
                            break;
                          }
                      }
                      #endif
                      
                      // выключим купюроприемник
                      if (IsValidatorConnected()) CC_CmdBillType(0x000000, 0x000000, ADDR_FL);
                      CoinDisable();
                      
                      // напечатаем чек
                      if (IsFiscalConnected())
                      {
                          CPU_INT32U format;
                          char service_name[48];
                          GetData(&BillFormatDesc, &format, 0, DATA_FLAG_SYSTEM_INDEX);
                          GetDataStr(&ServiceNameDesc, (CPU_INT08U*)service_name, 0, DATA_FLAG_SYSTEM_INDEX);
                          sprintf(&service_name[strlen(service_name)], " пост %d", RecentChannel + 1);
                          if (format == 0)
                          {
                              strcat(service_name, ", мин.");
                          }

                          if (PrintFiscalBill(accmoney, ChannelsPayedTime[RecentChannel], terminal_pay_flag, service_name, price) == 0)
                          {
                              SaveEventRecord(RecentChannel, JOURNAL_EVENT_PRINT_BILL, GetTimeSec());
                          }
                          else
                          {
                              // ошибка печати чека
                          }
                      }
                      
                      IncCounter(RecentChannel, ChannelsPayedTime[RecentChannel], accmoney, terminal_pay_flag);
                      SetAcceptedMoney(0);
                      terminal_pay_flag = 0;
                      
                      // канал блокируем при соответствующей настройке
                      CPU_INT32U protect_en = 0;
                      GetData(&SelectProtectDesc, &protect_en, RecentChannel, DATA_FLAG_DIRECT_INDEX);  
                      if (protect_en)
                      {
                          ChannelsState[RecentChannel] = CHANNEL_STATE_BUSY;
                          ChannelsCounters[RecentChannel] = ChannelsPayedTime[RecentChannel] + imp_pause;
                      }
                      
                      // сохраняем запись события
                      SaveEventRecord(RecentChannel, JOURNAL_EVENT_START_SESSION, ChannelsPayedTime[RecentChannel]);
                      
                      // повесим меню "СПАСИБО"                      
                      last_money = accmoney;
                      last_post = RecentChannel;
                      UserPrintThanksMenu();
                      RefreshMenu();
                      UserMenuState = USER_STATE_SHOW_THANKS;
                      ThanksCtr = 4;
                    }
                  else
                    { // денег недостаточно для работы
                      
                    
                    
                    }
                  break;
                }
                #endif
                  
              break;
              
          case EVENT_REREAD_SIGNAL_LEVEL:
              #if !APP_DEBUG
              {
                  CPU_INT32U en;
                  GetData(&EnableChannelDesc, &en, ChannelIndex, DATA_FLAG_DIRECT_INDEX);
                  if (en)
                  {
                      CPU_INT32U ip;
                      GetData(&PostIpAddrDesc, &ip, ChannelIndex, DATA_FLAG_DIRECT_INDEX);   
                      char param_str[16];
                      if (HostReadParam(ip, CONSOLE_TCP_DEFAULT_PORT, "SIGNAL", param_str, 16, 500) == 0)
                      {
                          int value = -1;
                          if ((sscanf(param_str, "%d\n", &value) == 1) && ((value == 0) || (value == 1)))
                          {
                              CPU_INT32U signal = value;
                              WriteArrayFram(offsetof(TFramMap, ChannelConfig.signal) + ChannelIndex*sizeof(CPU_INT32U), sizeof(CPU_INT32U), (unsigned char*)&signal);
                          }
                          
                          PostStatus[ChannelIndex] = 1;
                      }
                      else
                      {
                          PostStatus[ChannelIndex] = 0;
                      }
                  }
                  else
                  {
                      PostStatus[ChannelIndex] = 0;
                  }
              }
              #endif
              break;
                  
#elif defined(BOARD_POST_CARWASH)

          case EVENT_SEC:
              {
                #if OS_CRITICAL_METHOD == 3
                OS_CPU_SR  cpu_sr = 0;
                #endif
                int count;

                OS_ENTER_CRITICAL();
                if (app_timer_value == 0)
                {
                    if (pre_pause_counter) pre_pause_counter--;
                }
                else
                {
                    if (pre_pause_counter)
                    {
                        pre_pause_counter--;
                    }
                    else
                    {
                        if (out_timer_counter)
                        {
                            out_timer_counter--;
                            if (config_ram->signal) FIO0SET_bit.P0_24= 1;
                            else FIO0CLR_bit.P0_24= 1;
                        }
                        else
                        {
                            if (out_pulse_count)
                            {
                                // обновляем отсчет
                                out_timer_counter = app_timer_value;
                                if (config_ram->signal) FIO0SET_bit.P0_24= 1;
                                else FIO0CLR_bit.P0_24= 1;
                                out_pulse_count--;
                            }
                            else
                            {
                                // конец длительности
                                if (config_ram->signal) FIO0CLR_bit.P0_24= 1;
                                else FIO0SET_bit.P0_24= 1;
                                app_timer_value = 0;
                                out_timer_counter = 0;
                            }
                            if (out_timer_counter) out_timer_counter--;
                        }
                    }
                }
                count = out_pulse_count;
                OS_EXIT_CRITICAL();
                
                if (app_timer_value == 0)
                {
                    if (count > 0)
                    {
                        if (GetKbrdState() & (1UL << KEY_START))
                        {
                            #if OS_CRITICAL_METHOD == 3
                            OS_CPU_SR  cpu_sr = 0;
                            #endif
                            OS_ENTER_CRITICAL();
                            pre_pause_counter = 0;
                            OS_EXIT_CRITICAL();
                        }
                        
                        if (pre_pause_counter == 0)
                        {
                            PostUserEvent(EVENT_PULSEOUT);
                        }
                    }
                }

                test_counter++;
              }
                break;
          case EVENT_KEY_START:
              {
                #if OS_CRITICAL_METHOD == 3
                OS_CPU_SR  cpu_sr = 0;
                #endif
                OS_ENTER_CRITICAL();
                pre_pause_counter = 0;
                OS_EXIT_CRITICAL();
                PostUserEvent(EVENT_PULSEOUT);
                break;
              }
          case EVENT_PULSEOUT:
              {
                if ((app_timer_value == 0) && (!pre_pause_counter))
                {
                    #if OS_CRITICAL_METHOD == 3
                    OS_CPU_SR  cpu_sr = 0;
                    #endif
                    int count, len_ms, len_pause_ms;
    
                    OS_ENTER_CRITICAL();
                    count = out_pulse_count;
                    len_ms = out_pulse_len;
                    len_pause_ms = out_pulse_pause_len;
                    OS_EXIT_CRITICAL();
                    
                    if (count <= 0)
                    {
                        break;
                    }
                    
                    do
                    {
                        OS_ENTER_CRITICAL();
                        count = out_pulse_count;
                        len_ms = out_pulse_len;
                        len_pause_ms = out_pulse_pause_len;
                        OS_EXIT_CRITICAL();
    
                        if (config_ram->signal) FIO0SET_bit.P0_24= 1;
                        else FIO0CLR_bit.P0_24= 1;
                        OSTimeDly(len_ms);
                        if (config_ram->signal) FIO0CLR_bit.P0_24= 1;
                        else FIO0SET_bit.P0_24= 1;
                        OSTimeDly(len_pause_ms);
    
                        if (count)
                        {
                            OS_ENTER_CRITICAL();
                            out_pulse_count--;
                            count = out_pulse_count;
                            OS_EXIT_CRITICAL();
                        }
                    } while (count);
                }
                else
                {
                    test_counter = 0;
                }
              }
              break;
#endif
#if !defined(BOARD_SOLARIUM_VLAD)
            default:
              break;
          }
#endif
        }
      else 
        {
          OSTimeDly(1);
        }
    }
}


void StartUserApplication(void)
{
    // создадим очередь и задачу
    if (UserQuery == NULL)
    {    
        INT8U err;
        UserQuery = OSQCreate(&UserTbl[0], USER_QUERY_LEN);
        OSTaskCreate(UserAppTask, (void *)0, (OS_STK *)&UserTaskStk[USER_TASK_STK_SIZE-1], USER_TASK_PRIO);
        OSTaskNameSet(USER_TASK_PRIO, "app_serv Task", &err);
    }
}


/*!
 Пользовательская инициализация
*/
void UserStartupFunc(void)
{
#if defined(BOARD_SOLARIUM_VLAD)
    InitControls();
#endif
        
#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_WEB) || defined(BOARD_SOLARIUM_VLAD)
  // инициализация режима работы
  InitMode();
  // инициализация данных
  CheckAllData();
#endif
 
  JournalInit();
      
#if defined(CONFIG_MUSIC_ENABLE)
  player_init();
#endif
   
#if defined(CONFIG_SDCARD_ENABLE)
  sd_init();
#endif
       
#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_VLAD)
  InitPrecheckText();
#endif

#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_WEB) || defined(BOARD_SOLARIUM_VLAD)
  OnChangeInitByDefault();
#endif
  
#if defined(BOARD_CENTRAL_CARWASH)
  CheckKeysCorrect();
  
  OnChangeKeyAction();
#endif
      
#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_WEB) || defined(BOARD_SOLARIUM_VLAD)
  CheckLongCounters();
#endif
  
#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_VLAD)
  // восстановим деньги
  LoadAcceptedMoney();
#endif

#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_WEB) || defined(BOARD_SOLARIUM_VLAD)
  // проверим пароль
  InitPass();
#endif

#if defined(BOARD_SOLARIUM_WEB)
  // включение ламп
  PINSEL1_bit.P0_24 = 0x0;
  PINMODE1_bit.P0_24 = 0;
  FIO0DIR_bit.P0_24 = 1;
  FIO0MASK_bit.P0_24 = 0;
  FIO0CLR_bit.P0_24 = 1;
  // вентиляция
  PINSEL1_bit.P0_23 = 0x0;
  PINMODE1_bit.P0_23 = 0;
  FIO0DIR_bit.P0_23 = 1;
  FIO0MASK_bit.P0_23 = 0;
  FIO0CLR_bit.P0_23 = 1;
  // зуммер
  PINSEL3_bit.P1_21 = 0x0;
  PINMODE3_bit.P1_21 = 0;
  FIO1DIR_bit.P1_21 = 1;
  FIO1MASK_bit.P1_21 = 0;
  FIO1SET_bit.P1_21 = 1;
#endif
    
#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_WEB) || defined(BOARD_SOLARIUM_VLAD)
  InitMenu();
  OSTimeDly(1000);
#endif

#if defined(BOARD_SOLARIUM_VLAD)
    LoadControlChannels();
#endif
    
#if defined(CONFIG_MUSIC_ENABLE)
  InitMusicTask();
#endif
      
#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_VLAD)
  // запустим валидатор
  StartUpValidator();

  OSTimeDly(1000);

  CPU_INT32U sound_type;
  GetData(&SoundTypeDesc, &sound_type, 0, DATA_FLAG_SYSTEM_INDEX);
  
#if defined(CONFIG_MUSIC_ENABLE)
  if (sound_type == 1)
  {
      PlayMusicFile(MUSIC_FILE_HARDWARE_CHECKOUT);
  }
  else if (sound_type == 2)
  {
      CPU_INT32U ctr = 30;
      while (ctr--)
      {
          PlayMusicFile(MUSIC_FILE_HARDWARE_CHECKOUT);
          if (player_conn) break;
          OSTimeDly(3000);
      }
  }
#endif
      
  OSTimeDly(9000);
  InitFiscal();
#endif

#if defined(CONFIG_SCORE_ENABLE)
  InitScoreTask();
#endif
  
  // проинициализируем часы
  InitRTC();
  OSTimeDly(1100);

#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_WEB)  || defined(BOARD_SOLARIUM_VLAD)
  // сделаем запись о включении
  SaveEventRecord(0, JOURNAL_EVENT_DEVICE_ON, GetTimeSec());
#endif

  // инициализация модема
#ifdef CONFIG_MODEM_ENABLE
  if (InitModem() != 0)
  {
    SetErrorFlag(ERROR_MODEM_CONN);
  }
  else
  {
    ClrErrorFlag(ERROR_MODEM_CONN);
  }
#endif

#if defined(BOARD_CENTRAL_CARWASH) || defined(BOARD_SOLARIUM_VLAD)
  // запустим монетник
  InitCoin();
#endif

  SystemTime = GetTimeSec();
  
#if defined(BOARD_POST_CARWASH)
  PINSEL1_bit.P0_24 = 0x0;
  PINMODE1_bit.P0_24 = 0;
  FIO0DIR_bit.P0_24 = 1;
  FIO0MASK_bit.P0_24 = 0;
  if (config_ram->signal) FIO0CLR_bit.P0_24= 1;
  else FIO0SET_bit.P0_24= 1;
#endif
  
#ifdef CONFIG_WEB_INTERFACE
  httpd_init();
#endif

#if defined(CONFIG_TCP_CONSOLE)
  InitConsole();
#endif
  
#if defined(CONFIG_TERMINAL_ENABLE)
  InitTerminalApp();  
#endif
 
#if defined(BOARD_CENTRAL_CARWASH)
  if (GetMode() == MODE_WORK) {SetMenu(WORK_MENU);}
  else SetMenu(SERVICE_MENU);
#elif  defined(BOARD_SOLARIUM_VLAD)
  CPU_INT32U en_pass = 0;
  GetData(&EnableGlobalPassDesc, &en_pass, 0, DATA_FLAG_SYSTEM_INDEX);
  if (en_pass)
  {
      start_pass_ok = 0;
      SetMenu(START_PASS_MENU);
  }
  else
  {
      start_pass_ok = 1;
      if (GetMode() == MODE_WORK) {SetMenu(WORK_MENU);}
      else SetMenu(SERVICE_MENU);
  }
#elif  defined(BOARD_SOLARIUM_WEB)
  SetMenu(SERVICE_MENU);
  OnZummerEnabledChange();
  http_last_status_time = SystemTime;
#endif

}

int GetUserEvent(int* event)
{
  CPU_INT08U err = 0; 
  int evt  = (int)OSQPend(UserQuery, 1, &err);
  if (err != 0) return 0;
  *event = evt;
  return 1;  
}   


void PostUserEvent(int event)
{
  OSQPost(UserQuery, (void *)event);
}


void InitUserMenu(void)
{
  for (int i = 0; i < CHANNELS_NUM; i++)
  {
    CPU_INT32U en = 0;
    GetData(&EnableChannelDesc, &en, i, DATA_FLAG_DIRECT_INDEX);
    if (en)
    {
        ChannelsState[i] = CHANNEL_STATE_FREE;
    }
    else
    {
        ChannelsState[i] = CHANNEL_STATE_DISABLED;
    }
  }
  #if defined(BOARD_SOLARIUM_WEB)
  if (pass_edit)
  {
    SetMode(MODE_WORK);
    pass_edit = 0;
  }
  #endif
}

void UserPrintMoneyMenu(void)
{
    char buf[32];
    CPU_INT32U accmoney;    
    
    accmoney = GetAcceptedMoney();
    EnabledChannelsNum = 0;
    for (CPU_INT08U i = 0; i < CHANNELS_NUM; i++)
    {
        CPU_INT32U ch_en = 0;
        CPU_INT32U protect_en = 0;
        GetData(&SelectProtectDesc, &protect_en, i, DATA_FLAG_DIRECT_INDEX);  
        GetData(&EnableChannelDesc, &ch_en, i, DATA_FLAG_DIRECT_INDEX);
        if ((ch_en) && !((ChannelsState[i] == CHANNEL_STATE_BUSY) && (protect_en)))
        {
            EnabledChannelsNum++;
            if (accmoney > 0) SetBacklight(LED_POST1 + i, 1);
            else SetBacklight(LED_POST1 + i, 0);
        }
        else
        {
            SetBacklight(LED_POST1 + i, 0);
        }
    }

    if (EnabledChannelsNum == 0)
    {
        // нет включенных каналов - работа невозможна
        sprintf(buf, "");
        PrintUserMenuStr(buf, 0);
        sprintf(buf, "   ВСЕ ПОСТЫ");
        PrintUserMenuStr(buf, 1);
        sprintf(buf, "    ЗАНЯТЫ");
        PrintUserMenuStr(buf, 2);
        sprintf(buf, "");
        PrintUserMenuStr(buf, 3);
        if (++blink_counter & 0x1)
        {
            SetBacklight(LED_OK, 1);
            SetBacklight(LED_CANSEL, 0);
        }
        else
        {
            SetBacklight(LED_OK, 0);
            SetBacklight(LED_CANSEL, 1);
        }
        return;   
    }
    
    SetBacklight(LED_OK, 1);
    SetBacklight(LED_CANSEL, 1);
    
    sprintf(buf, " Внесите деньги");
    PrintUserMenuStr(buf, 0);
    sprintf(buf, " и выберите пост");
    PrintUserMenuStr(buf, 1);
    accmoney = GetAcceptedMoney();
    sprintf(buf, " Принято %d руб.", accmoney);
    PrintUserMenuStr(buf, 2);
    sprintf(buf, " ");
    PrintUserMenuStr(buf, 3);
}

void UserPrintSelectMoneyMenu()
{
    CPU_INT32U money[2];
    char buf[32];
    int len;
    int row = 0;

    EnabledChannelsNum = 0;
    for (CPU_INT08U i = 0; i < CHANNELS_NUM; i++)
    {
        CPU_INT32U ch_en = 0;
        CPU_INT32U protect_en = 0;
        GetData(&SelectProtectDesc, &protect_en, i, DATA_FLAG_DIRECT_INDEX);  
        GetData(&EnableChannelDesc, &ch_en, i, DATA_FLAG_DIRECT_INDEX);
        if ((ch_en) && !((ChannelsState[i] == CHANNEL_STATE_BUSY) && (protect_en)))
        {
            EnabledChannelsNum++;
            //SetBacklight(LED_POST1 + i, 1);
        }
        else
        {
            //SetBacklight(LED_POST1 + i, 0);
        }
    }

    if (EnabledChannelsNum == 0)
    {
        // нет включенных каналов - работа невозможна
        sprintf(buf, "");
        PrintUserMenuStr(buf, 0);
        sprintf(buf, "   ВСЕ ПОСТЫ");
        PrintUserMenuStr(buf, 1);
        sprintf(buf, "    ЗАНЯТЫ");
        PrintUserMenuStr(buf, 2);
        sprintf(buf, "");
        PrintUserMenuStr(buf, 3);
        if (++blink_counter & 0x1)
        {
            SetBacklight(LED_OK, 1);
            SetBacklight(LED_CANSEL, 0);
        }
        else
        {
            SetBacklight(LED_OK, 0);
            SetBacklight(LED_CANSEL, 1);
        }
        return;   
    }

    SetBacklight(LED_OK, 1);
    SetBacklight(LED_CANSEL, 1);

    GetData(&SelectedMoneyDesc, &money[0], 3, DATA_FLAG_DIRECT_INDEX);   
    GetData(&SelectedMoneyDesc, &money[1], 7, DATA_FLAG_DIRECT_INDEX);   
    if (money[0] || money[1])
    {
        memset(buf, 0, 32);
        sprintf(buf, "4-%dр.", money[0]);
        len = strlen(buf);
        if (len < 9)
        {
            memset(&buf[len], ' ', 9 - len);
        }
        sprintf(&buf[9], "8-%dр.", money[1]);
        len = strlen(buf);
        if (len < 19)
        {
            memset(&buf[len], ' ', 19 - len);
        }
        PrintUserMenuStr(buf, 3);
        SetBacklight(LED_POST4, (money[0] != 0));
        SetBacklight(LED_POST8, (money[1] != 0));
    }
    else
    {
        sprintf(buf, "    ВЫБОР СУММЫ");
        PrintUserMenuStr(buf, 0);
        row = 1;
    }

    GetData(&SelectedMoneyDesc, &money[0], 0, DATA_FLAG_DIRECT_INDEX);   
    GetData(&SelectedMoneyDesc, &money[1], 4, DATA_FLAG_DIRECT_INDEX);   
    memset(buf, 0, 32);
    sprintf(buf, "1-%dр.", money[0]);
    len = strlen(buf);
    if (len < 9)
    {
        memset(&buf[len], ' ', 9 - len);
    }
    sprintf(&buf[9], "5-%dр.", money[1]);
    len = strlen(buf);
    if (len < 19)
    {
        memset(&buf[len], ' ', 19 - len);
    }
    PrintUserMenuStr(buf, row++);
    SetBacklight(LED_POST1, (money[0] != 0));
    SetBacklight(LED_POST5, (money[1] != 0));
    
    GetData(&SelectedMoneyDesc, &money[0], 1, DATA_FLAG_DIRECT_INDEX);   
    GetData(&SelectedMoneyDesc, &money[1], 5, DATA_FLAG_DIRECT_INDEX);   
    memset(buf, 0, 32);
    sprintf(buf, "2-%dр.", money[0]);
    len = strlen(buf);
    if (len < 9)
    {
        memset(&buf[len], ' ', 9 - len);
    }
    sprintf(&buf[9], "6-%dр.", money[1]);
    len = strlen(buf);
    if (len < 19)
    {
        memset(&buf[len], ' ', 19 - len);
    }
    PrintUserMenuStr(buf, row++);
    SetBacklight(LED_POST2, (money[0] != 0));
    SetBacklight(LED_POST6, (money[1] != 0));

    GetData(&SelectedMoneyDesc, &money[0], 2, DATA_FLAG_DIRECT_INDEX);   
    GetData(&SelectedMoneyDesc, &money[1], 6, DATA_FLAG_DIRECT_INDEX);   
    memset(buf, 0, 32);
    sprintf(buf, "3-%dр.", money[0]);
    len = strlen(buf);
    if (len < 9)
    {
        memset(&buf[len], ' ', 9 - len);
    }
    sprintf(&buf[9], "7-%dр.", money[1]);
    len = strlen(buf);
    if (len < 19)
    {
        memset(&buf[len], ' ', 19 - len);
    }
    PrintUserMenuStr(buf, row++);
    SetBacklight(LED_POST3, (money[0] != 0));
    SetBacklight(LED_POST7, (money[1] != 0));    
}

void UserPrintSelectPayTypeMenu(void)
{
    char buf[32];

    EnabledChannelsNum = 0;
    for (CPU_INT08U i = 0; i < CHANNELS_NUM; i++)
    {
        CPU_INT32U ch_en = 0;
        CPU_INT32U protect_en = 0;
        GetData(&SelectProtectDesc, &protect_en, i, DATA_FLAG_DIRECT_INDEX);  
        GetData(&EnableChannelDesc, &ch_en, i, DATA_FLAG_DIRECT_INDEX);
        if ((ch_en) && !((ChannelsState[i] == CHANNEL_STATE_BUSY) && (protect_en)))
        {
            EnabledChannelsNum++;
            SetBacklight(LED_POST1 + i, 0);
        }
        else
        {
            SetBacklight(LED_POST1 + i, 0);
        }
    }

    if (EnabledChannelsNum == 0)
    {
        // нет включенных каналов - работа невозможна
        sprintf(buf, "");
        PrintUserMenuStr(buf, 0);
        sprintf(buf, "   ВСЕ ПОСТЫ");
        PrintUserMenuStr(buf, 1);
        sprintf(buf, "    ЗАНЯТЫ");
        PrintUserMenuStr(buf, 2);
        sprintf(buf, "");
        PrintUserMenuStr(buf, 3);
        if (++blink_counter & 0x1)
        {
            SetBacklight(LED_OK, 1);
            SetBacklight(LED_CANSEL, 0);
        }
        else
        {
            SetBacklight(LED_OK, 0);
            SetBacklight(LED_CANSEL, 1);
        }
        return;   
    }

    SetBacklight(LED_OK, 1);
    SetBacklight(LED_CANSEL, 1);
    
    sprintf(buf, " ВАРИАНТЫ ОПЛАТЫ");
    PrintUserMenuStr(buf, 0);
    sprintf(buf, " ");
    PrintUserMenuStr(buf, 1);
    #ifdef BOARD_SWAP_OK_CANSEL
    sprintf(buf, "НАЛИЧНЫЕ    ПО КАРТЕ");
    #else
    sprintf(buf, "ПО КАРТЕ    НАЛИЧНЫЕ");
    #endif
    PrintUserMenuStr(buf, 2);
    sprintf(buf, " ");
    PrintUserMenuStr(buf, 3);
}

void UserPrintTerminalRequestMenu()
{
    char buf[32];

    SetBacklight(LED_POST1, 1);
    SetBacklight(LED_POST2, 1);
    SetBacklight(LED_POST3, 1);
    SetBacklight(LED_POST4, 1);
    SetBacklight(LED_POST5, 1);
    SetBacklight(LED_POST6, 1);
    SetBacklight(LED_POST7, 1);
    SetBacklight(LED_POST8, 1);
    SetBacklight(LED_OK, 1);
    SetBacklight(LED_CANSEL, 1);
    
    sprintf(buf, "ОПЛАТА %d рублей...", TerminalMoney);
    PrintUserMenuStr(buf, 0);

    CPU_INT32U proto;
    GetData(&TerminalProtocolDesc, &proto, 0, DATA_FLAG_SYSTEM_INDEX);
    if (proto == TERMINAL_PROTOCOL_TTK2)
    {
        sprintf(buf, "     Вставьте ");
        PrintUserMenuStr(buf, 1);
        sprintf(buf, "  банковскую карту");
        PrintUserMenuStr(buf, 2);
        sprintf(buf, "   в картоприемник");
        PrintUserMenuStr(buf, 3);
    }
    else // if (proto == TERMINAL_PROTOCOL_VTK)
    { 
        sprintf(buf, "     Поднесите");
        PrintUserMenuStr(buf, 1);
        sprintf(buf, "  банковскую карту");
        PrintUserMenuStr(buf, 2);
        sprintf(buf, "   к считывателю");
        PrintUserMenuStr(buf, 3);
    }
}

void UserPrintIpDeviceErrMenu(CPU_INT08U post)
{
    char buf[32];
    sprintf(buf, " ");
    PrintUserMenuStr(buf, 0);
    sprintf(buf, "    ПОСТ %d", post + 1);
    PrintUserMenuStr(buf, 1);
    sprintf(buf, "   НЕДОСТУПЕН");
    PrintUserMenuStr(buf, 2);
    sprintf(buf, " ");
    PrintUserMenuStr(buf, 3);
}

// вывод меню о невозможости работы
void UserPrintResultMenu(void)
{
  char buf[32];
  
  if (TstErrorFlag(ERROR_FR_CONN))
    {
      sprintf(buf, "ОШИБКА");
      PrintUserMenuStr(buf, 0);
      sprintf(buf, "НЕТ СВЯЗИ С ФР");
      PrintUserMenuStr(buf, 1);
      sprintf(buf, "");
      PrintUserMenuStr(buf, 2);
      PrintUserMenuStr(buf, 3);
    }
  else if (TstCriticalFiscalError())
    {
      sprintf(buf, "ОШИБКА");
      PrintUserMenuStr(buf, 0);
      CPU_INT08U errcode = 0;
      sprintf(buf, "ОШИБКА ФР");
      PrintUserMenuStr(buf, 1);
      GetFirstCriticalFiscalError(&errcode);
      GetDataItem(&JournalErrorNumberDesc0, (CPU_INT08U*)buf, errcode);
      PrintUserMenuStr(buf, 2);
      GetDataItem(&JournalErrorNumberDesc1, (CPU_INT08U*)buf, errcode);
      PrintUserMenuStr(buf, 3);
    }
  else
  {
      sprintf(buf, " ");
      PrintUserMenuStr(buf, 0);
      sprintf(buf, "КОМАНДА");
      PrintUserMenuStr(buf, 1);
      sprintf(buf, "ВЫПОЛНЕНА");
      PrintUserMenuStr(buf, 2);
      sprintf(buf, "");
      PrintUserMenuStr(buf, 3);
  }
}
// вывод меню о невозможости работы
void UserPrintErrorMenu(void)
{
  char buf[32];
  
  if (TstErrorFlag(ERROR_VALIDATOR_CONN) || TstCriticalValidatorErrors())
    {
      sprintf(buf, "ОШИБКА");
      PrintUserMenuStr(buf, 0);
      sprintf(buf, "КУПЮРОПРИЕМНИКА");
      PrintUserMenuStr(buf, 1);
      if (TstErrorFlag(ERROR_VALIDATOR_CONN)) 
        {
          sprintf(buf, "НЕТ СВЯЗИ");
          PrintUserMenuStr(buf, 2);
          sprintf(buf, "");
          PrintUserMenuStr(buf, 3);
        }
    }
  else if (TstErrorFlag(ERROR_FR_CONN))
    {
      sprintf(buf, "ОШИБКА");
      PrintUserMenuStr(buf, 0);
      sprintf(buf, "НЕТ СВЯЗИ С ФР");
      PrintUserMenuStr(buf, 1);
      sprintf(buf, "");
      PrintUserMenuStr(buf, 2);
      PrintUserMenuStr(buf, 3);
    }
  else if (TstCriticalFiscalError())
    {
      sprintf(buf, "ОШИБКА");
      PrintUserMenuStr(buf, 0);
      CPU_INT08U errcode = 0;
      sprintf(buf, "ОШИБКА ФР");
      PrintUserMenuStr(buf, 1);
      GetFirstCriticalFiscalError(&errcode);
      GetDataItem(&JournalErrorNumberDesc0, (CPU_INT08U*)buf, errcode);
      PrintUserMenuStr(buf, 2);
      GetDataItem(&JournalErrorNumberDesc1, (CPU_INT08U*)buf, errcode);
      PrintUserMenuStr(buf, 3);
    }
  /*  
  else if (!FReportTest())
    {
      sprintf(buf, "ПЕЧАТb ОТЧЕТА");
      PrintUserMenuStr(buf, 0);
      sprintf(buf, "ЖДИТЕ");
      PrintUserMenuStr(buf, 1);
      sprintf(buf, "ПРИНТЕР");
      PrintUserMenuStr(buf, 2);
      sprintf(buf, "НЕДОСТУПЕН");
      PrintUserMenuStr(buf, 3);
    }
  */
}

int CheckChannelEnabled(CPU_INT08U channel)
{
    CPU_INT32U en = 0;
    GetData(&EnableChannelDesc, &en, channel, DATA_FLAG_DIRECT_INDEX);
    if (en)
    {
        return 1;
    }
    return 0;
}


void WorkServer(void)
{
  for (CPU_INT08U i=0; i<CHANNELS_NUM; i++)
    {
      switch (ChannelsState[i])
      {
        case CHANNEL_STATE_FREE:
          if ((UserMenuState != USER_STATE_ACCEPT_MONEY) && (UserMenuState != USER_STATE_SELECT_MONEY) && (UserMenuState != USER_STATE_TERMINAL_REQUEST)
                && !(ChannelsState[i] == CHANNEL_STATE_BUSY))
          {
            if (GetMode() != MODE_WORK) SetBacklight(LED_POST1 + i, 1);
          }
          break;  
        case CHANNEL_STATE_DISABLED:
          if ((UserMenuState != USER_STATE_ACCEPT_MONEY) && (UserMenuState != USER_STATE_SELECT_MONEY) && (UserMenuState != USER_STATE_TERMINAL_REQUEST))
          {
            if (GetMode() != MODE_WORK) SetBacklight(LED_POST1 + i, 0);
          }
          break;
        case CHANNEL_STATE_BUSY:
          if ((UserMenuState != USER_STATE_ACCEPT_MONEY) && (UserMenuState != USER_STATE_SELECT_MONEY) && (UserMenuState != USER_STATE_TERMINAL_REQUEST))
          {
            if (GetMode() != MODE_WORK) SetBacklight(LED_POST1 + i, 0);
          }
          
          if (ChannelsCounters[i]) ChannelsCounters[i]--;
          if (!ChannelsCounters[i])
            {
              ChannelsState[i] = CHANNEL_STATE_FREE;
              // запишем событие
              SaveEventRecord(i, JOURNAL_EVENT_END_SESSION, GetTimeSec());
            }
          break;
      }
    
    }
  
}


void UserPrintThanksMenu(void)
{
  char buf[32];
  sprintf(buf, "      ПОСТ %d", last_post + 1);
  PrintUserMenuStr(buf, 0);
  sprintf(buf, "  Оплата %d руб.", last_money);
  PrintUserMenuStr(buf, 1);
  sprintf(buf, "     Принята");
  PrintUserMenuStr(buf, 2);
  sprintf(buf, " ");
  PrintUserMenuStr(buf, 3);
}

void UserPrintPaymentErrorMenu(void)
{
  char buf[32];
  sprintf(buf, " ");
  PrintUserMenuStr(buf, 0);
  sprintf(buf, "      ОТКАЗ");
  PrintUserMenuStr(buf, 1);
  sprintf(buf, " ");
  PrintUserMenuStr(buf, 2);
  sprintf(buf, " ");
  PrintUserMenuStr(buf, 3);
}

void UserPrintAbonementPaymentErrorMenu(void)
{
  char buf[32];
  sprintf(buf, " По этой КАРТЕ ЗАГАРА");
  PrintUserMenuStr(buf, 0);
  sprintf(buf, "   сегодня посещения");
  PrintUserMenuStr(buf, 1);
  sprintf(buf, "       исчерпаны");
  PrintUserMenuStr(buf, 2);
  sprintf(buf, " ");
  PrintUserMenuStr(buf, 3);
}

int ChannelBusy(CPU_INT08U ch)
{
    CPU_INT32U ch_en = 0;
    CPU_INT32U protect_en = 0;
    GetData(&SelectProtectDesc, &protect_en, ch, DATA_FLAG_DIRECT_INDEX);  
    GetData(&EnableChannelDesc, &ch_en, ch, DATA_FLAG_DIRECT_INDEX);
    if ((ch_en) && ((ChannelsState[ch] == CHANNEL_STATE_BUSY) && (protect_en)))
    {
        return 1;
    }
    return 0;
}
            
void UserPrintSelectPostMenu(void)
{
    char buf[32];

    EnabledChannelsNum = 0;
    for (CPU_INT08U i = 0; i < CHANNELS_NUM; i++)
    {
        CPU_INT32U ch_en = 0;
        CPU_INT32U protect_en = 0;
        GetData(&SelectProtectDesc, &protect_en, i, DATA_FLAG_DIRECT_INDEX);  
        GetData(&EnableChannelDesc, &ch_en, i, DATA_FLAG_DIRECT_INDEX);
        if ((ch_en) && !((ChannelsState[i] == CHANNEL_STATE_BUSY) && (protect_en)))
        {
            EnabledChannelsNum++;
            SetBacklight(LED_POST1 + i, 1);
        }
        else
        {
            SetBacklight(LED_POST1 + i, 0);
        }
    }

    if (EnabledChannelsNum == 0)
    {
        // нет включенных каналов - работа невозможна
        sprintf(buf, "");
        PrintUserMenuStr(buf, 0);
        sprintf(buf, "   ВСЕ ПОСТЫ");
        PrintUserMenuStr(buf, 1);
        sprintf(buf, "    ЗАНЯТЫ");
        PrintUserMenuStr(buf, 2);
        sprintf(buf, "");
        PrintUserMenuStr(buf, 3);
        if (++blink_counter & 0x1)
        {
            SetBacklight(LED_OK, 1);
            SetBacklight(LED_CANSEL, 0);
        }
        else
        {
            SetBacklight(LED_OK, 0);
            SetBacklight(LED_CANSEL, 1);
        }
        return;   
    }
       
    sprintf(buf, " ");
    PrintUserMenuStr(buf, 0);
    sprintf(buf, "    ВЫБЕРИТЕ");
    PrintUserMenuStr(buf, 1);
    sprintf(buf, "      ПОСТ");
    PrintUserMenuStr(buf, 2);
    sprintf(buf, " ");
    PrintUserMenuStr(buf, 3);
}

#if defined(BOARD_SOLARIUM_WEB)

void PrintServerDisconnectedMenu()
{
    PrintUserMenuStrUnalign("     СЕРВЕР", 0);
    PrintUserMenuStrUnalign("   НЕДОСТУПЕН", 1);
    RefreshMenu(); 
}


void PrintSolariumBlockingMessage()
{
    char buf[32];
    strncpy(buf, message_lines[0], 16);
    PrintUserMenuStr(buf, 0);
    strncpy(buf, message_lines[1], 16);
    PrintUserMenuStr(buf, 1);
    RefreshMenu(); 
}

void PrintSolariumFreeMenu()
{
    if (show_blocking_msg) return;
    PrintUserMenuStrUnalign("    СОЛЯРИй", 0);
    PrintUserMenuStrUnalign("    СВОБОДЕН", 1);
    RefreshMenu(); 
}

void PrintSolariumPassMenu()
{
    char buf[32];
    sprintf(buf, "  %s", pass_phrase);
    PrintUserMenuStr(buf, 0);
    sprintf(buf, "");
    PrintUserMenuStr(buf, 1);
    RefreshMenu(); 
}

void SolariumStartUserInput()
{
    CPU_INT32U mintime, maxtime;
    CPU_INT32U usertime;
    GetData(&SolariumMinTimeDesc, &mintime, 0, DATA_FLAG_SYSTEM_INDEX);    
    GetData(&SolariumMaxTimeDesc, &maxtime, 0, DATA_FLAG_SYSTEM_INDEX);    
    
    if (app_state.timefromserver > 0)
    {
        usertime = app_state.timefromserver;
    }
    else if (app_state.moneyfromserver > 0)
    {
        usertime = CalcTimeSecPerMoney(app_state.moneyfromserver);
    }
    else
    {
        usertime = 0;
    }
    
    if (usertime > maxtime * 60)
    {
        usertime = maxtime * 60;
    }
    
    if (usertime > 99 * 60UL)
    {
        usertime = 99 * 60UL;
    }
    
    PrintSecToMinSec(user_input_str, usertime);
    user_input_ctr = 0;
}

CPU_INT32U SolariumGetUserInputTime()
{
    int min = 0, sec = 0;

    if (sscanf(user_input_str, "%02d:%02d", &min, &sec) == 2)
    {
        return min * 60UL + sec;
    }
    return 0;
}

CPU_INT32U SolariumFinishUserInput()
{
    int min = 0, sec = 0;

    MenuCursorOff();

    if (sscanf(user_input_str, "%02d:%02d", &min, &sec) == 2)
    {
        return min * 60UL + sec;
    }
    return 0;
}

void PrintSolariumUserInputMenu()
{
    char str[32];
    char str_time[16];
    
    if (show_blocking_msg) return;
        
    if (app_state.timefromserver > 0)
    {
        PrintSecToMinSec(str_time, app_state.timefromserver);
    }
    else if (app_state.moneyfromserver > 0)
    {
        PrintSecToMinSec(str_time, CalcTimeSecPerMoney(app_state.moneyfromserver));
    }
    else
    {
        PrintSecToMinSec(str_time, 0);
    }
    
    strcpy(str, "Доступно ");
    strcpy(&str[strlen(str)], str_time);
    PrintUserMenuStr(str, 0);
    
    strcpy(str, user_input_str);
    PrintUserMenuStr(str, 1);
    
    MenuCursorOn(5 + user_input_pos[user_input_ctr], 1);
        
    RefreshMenu(); 
}

void PrintSolariumSunburnMenu()
{
    char buf[32];

    if (show_blocking_msg) return;
    
    PrintUserMenuStrUnalign("  СЕАНС ЗАГАРА", 0);
    
    PrintSecToMinSec(buf, app_state.timetoend);
    PrintUserMenuStr(buf, 1);
    RefreshMenu(); 
}

void PrintSolariumVentilationMenu()
{
    char buf[32];
    
    if (show_blocking_msg) return;
    
    PrintUserMenuStrUnalign("   ВЕНТИЛЯЦИЯ", 0);
    
    PrintSecToMinSec(buf, app_state.timetoend);
    PrintUserMenuStr(buf, 1);
    RefreshMenu(); 
}

void PrintSolariumPrePauseMenu()
{
    char buf[32];

    if (show_blocking_msg) return;
    
    sprintf(buf, "ДО НАЧАЛА СЕАНСА");
    PrintUserMenuStr(buf, 0);
    
    PrintSecToMinSec(buf, app_state.timetoend);
    PrintUserMenuStr(buf, 1);
    RefreshMenu(); 
}

void PrintWaitingAckUserMenu()
{
    if (show_blocking_msg) return;
    
    PrintUserMenuStrUnalign("    ОЖИДАНИЕ", 0);
    PrintUserMenuStrUnalign("    ОДОБРЕНИЯ", 1);
    RefreshMenu(); 
}


#endif // #if defined(BOARD_SOLARIUM_WEB)

// проверка, были ли сохранены деньги до выключения питания
void LoadAcceptedMoney(void)
{
  CPU_INT32U m,crc,crct;

  // считаем cохраненные деньги из FRAM
  GetData(&AcceptedMoneyDesc, &m, 0, DATA_FLAG_SYSTEM_INDEX);    
  // считаем crc16 этих денег из FRAM 
  GetData(&AcceptedMoneyCRC16Desc, &crc, 0, DATA_FLAG_SYSTEM_INDEX);    
  
  crct = crc16((unsigned char*)&m, sizeof(CPU_INT32U));

  if (crct != crc)
    { // обнуляем, если crc не сошлась
      m = 0;
      crc = crc16((unsigned char*)&m, sizeof(CPU_INT32U));
      SetData(&AcceptedMoneyDesc, &m, 0, DATA_FLAG_SYSTEM_INDEX);
      SetData(&AcceptedMoneyCRC16Desc, &crc, 0, DATA_FLAG_SYSTEM_INDEX);
    }
    
}

// добавить денег
void SetAcceptedMoney(CPU_INT32U money)
{
  CPU_INT32U m,crc;
  m=money;
  crc = crc16((unsigned char*)&m, sizeof(CPU_INT32U));
  SetData(&AcceptedMoneyDesc, &m, 0, DATA_FLAG_SYSTEM_INDEX);
  SetData(&AcceptedMoneyCRC16Desc, &crc, 0, DATA_FLAG_SYSTEM_INDEX);
}

// очистить счетчик денег
void ClearAcceptedMoney(void)
{
  CPU_INT32U m,crc;
  m=0;
  crc = crc16((unsigned char*)&m, sizeof(CPU_INT32U));
  SetData(&AcceptedMoneyDesc, &m, 0, DATA_FLAG_SYSTEM_INDEX);
  SetData(&AcceptedMoneyCRC16Desc, &crc, 0, DATA_FLAG_SYSTEM_INDEX);
}

// прочитать счетчик денег
CPU_INT32U GetAcceptedMoney(void)
{
  CPU_INT32U m;
  GetData(&AcceptedMoneyDesc, &m, 0, DATA_FLAG_SYSTEM_INDEX);
  return m;
}

void PrintCardBill(void)
{
    CPU_INT08U err;
        
    if (IsFiscalConnected())
    {
        char str[41];
        int tag_size, temp1, temp2;
        char str_short[16];
       
        FPend();
       
        sprintf(str, " ");
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

        for (int i = 0; i < PRETEXT_LINE_COUNT; i++)
        {
            if (ReadPrecheckText(str, i) > 0)
            {
                if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
                {
                    goto exit_print;
                }
            }
        }

        // 1. ОПЕРАЦИЯ
        memset(str, 0, 40);
        tag_size = ttk2_get_field_string(0x81, str, 40);
        if (tag_size <= 0)
        {
            sprintf(str, "--- НЕИЗВЕСТНО ---");
        }
        else if (strcmp(str, "PUR") == 0)
        {
            sprintf(str, "--- ПРОДАЖА ---");
        }
        else
        {
            sprintf(str, "--- ДРУГАЯ ОПЕРАЦИЯ ---");
        }

        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

        // 2. ИТОГО
        memset(str, 0, 40);
        sprintf(str, "ИТОГО:");
        tag_size = ttk2_get_field_string(0x84, &str[strlen(str)], 20);
        if (tag_size <= 0)
        {
            goto next_line0;
        }

        // 3. DECIMAL POINT
        memset(str_short, 0, 2);
        tag_size = ttk2_get_field_string(0xC2, str_short, 1);
        if (tag_size > 0)
        {
            temp1 = str_short[0] - '0';
            temp2 = strlen(str);
        }
        else
        {
            temp1 = 2;
            temp2 = strlen(str);
        }
            
        memcpy(&str[20], &str[temp2 - temp1], temp1);
        str[temp2 - temp1] = '.';
        strcpy(&str[temp2 - temp1 + 1], &str[20]);
        memset(&str[strlen(str)], 0, 40 - strlen(str));

        // 4. CURRENCY
        str[strlen(str)] = ' ';
        tag_size = ttk2_get_field_string(0xC1, &str[strlen(str)], 40 - strlen(str));

        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

    next_line0:
        // 5. ОПЕРАЦИЯ ОДОБРЕНА 
        memset(str_short, 0, 2);
        tag_size = ttk2_get_field_string(0xA1, str_short, 2);
        if (tag_size <= 0)
        {
            sprintf(str, "НЕИЗВЕСТНО");
        }
        else if (str_short[0] == 'Y')
        {
            sprintf(str, "ОПЕРАЦИЯ ОДОБРЕНА");
        }
        else
        {
            sprintf(str, "ОПЕРАЦИЯ ОТКЛОНЕНА");
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }
            
        // 6. Issuer Name
        memset(str, 0, 40);
        tag_size = ttk2_get_field_string(0x8F, str, 20);
        if (tag_size < 0)
        {
            goto next_line1;
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

    next_line1:        
        // 7. PAN
        memset(str, 0, 40);
        tag_size = ttk2_get_field_string(0x89, str, 20);
        if (tag_size < 0)
        {
            goto next_line2;
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }
        
    next_line2:
        // 8. POS Entry Mode
        memset(str_short, 0, 16);
        tag_size = ttk2_get_field_string(0x92, str_short, 16);
        if (tag_size < 0)
        {
            goto next_line3;
        }
        
        if ((str_short[0] == '9') && (str_short[1] == '0'))
        {
            sprintf(str, "FULL TRACK READ");
        }
        else if ((str_short[0] == '0') && (str_short[1] == '1'))
        {
            sprintf(str, "MANUAL PAN ENTRY");
        }
        else if ((str_short[0] == '0') && (str_short[1] == '5'))
        {
            sprintf(str, "EMV CHIP");
        }
        else if ((str_short[0] == '8') && (str_short[1] == '0'))
        {
            sprintf(str, "EMV FALLBACK");
        }
        else if ((str_short[0] == '0') && (str_short[1] == '7'))
        {
            sprintf(str, "CONTACTLESS EMV");
        }
        else if ((str_short[0] == '9') && (str_short[1] == '1'))
        {
            sprintf(str, "CONTACTLESS");
        }
        else
        {
            goto next_line3;
        }
        
        if (str_short[2] == '9')
        {
            sprintf(&str[strlen(str)], " OFFLINE");
        }

        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

    next_line3:
        // 9. Invoice Number
        memset(str, 0, 40);
        sprintf(str, "ЧЕК:");
        tag_size = ttk2_get_field_string(0x8B, &str[strlen(str)], 40 - strlen(str));
        if (tag_size < 0)
        {
            goto next_line4;
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

    next_line4:        
        // 10. Authorization ID
        memset(str, 0, 40);
        sprintf(str, "КОД АВТ:");
        tag_size = ttk2_get_field_string(0x8C, &str[strlen(str)], 40 - strlen(str));
        if (tag_size < 0)
        {
            goto next_line5;
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }
        
    next_line5:
        // 11. Время
        memset(str, 0, 40);
        memset(str_short, 0, 16);
        sprintf(str, "ДАТА:");
        tag_size = ttk2_get_field_string(0x8D, str_short, 16);
        if (tag_size == 8)
        {
            char *str_ptr = &str[strlen(str)];
            *str_ptr++ = str_short[0]; *str_ptr++ = str_short[1];
            *str_ptr++ = '-';
            *str_ptr++ = str_short[2]; *str_ptr++ = str_short[3];
            *str_ptr++ = '-';
            *str_ptr++ = str_short[4]; *str_ptr++ = str_short[5]; *str_ptr++ = str_short[6]; *str_ptr++ = str_short[7];
        }
        else if (tag_size == 4)
        {
            char *str_ptr = &str[strlen(str)];
            *str_ptr++ = str_short[0]; *str_ptr++ = str_short[1];
            *str_ptr++ = '-';
            *str_ptr++ = str_short[2]; *str_ptr++ = str_short[3];
        }        
        else
        {
            goto next_line6;
        }
        
        sprintf(&str[strlen(str)], " ");

        memset(str_short, 0, 16);
        tag_size = ttk2_get_field_string(0x8E, str_short, 16);
        if (tag_size == 6)
        {
            char *str_ptr = &str[strlen(str)];
            *str_ptr++ = str_short[0]; *str_ptr++ = str_short[1];
            *str_ptr++ = ':';
            *str_ptr++ = str_short[2]; *str_ptr++ = str_short[3];
            *str_ptr++ = ':';
            *str_ptr++ = str_short[4]; *str_ptr++ = str_short[5];
        }
        else if (tag_size == 4)
        {
            char *str_ptr = &str[strlen(str)];
            *str_ptr++ = str_short[0]; *str_ptr++ = str_short[1];
            *str_ptr++ = ':';
            *str_ptr++ = str_short[2]; *str_ptr++ = str_short[3];
        }
        else
        {
            goto next_line6;
        }
        
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }
        
    next_line6:
        // 12. ТЕРМИНАЛ
        memset(str, 0, 40);
        sprintf(str, "ТЕРМИНАЛ:");
        tag_size = ttk2_get_field_string(0x9D, &str[strlen(str)], 40 - strlen(str));
        if (tag_size < 0)
        {
            goto next_line7;
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

    next_line7:        
        // 13. MERCHANT
        memset(str, 0, 40);
        sprintf(str, "MERCHANT:");
        tag_size = ttk2_get_field_string(0x90, &str[strlen(str)], 40 - strlen(str));
        if (tag_size > 0)
        {
            if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
            {
                goto exit_print;
            }
        }

        // 14. Cardholder Verification Method
        memset(str_short, 0, 2);
        tag_size = ttk2_get_field_string(0x94, str_short, 2);
        if (tag_size <= 0)
        {
            goto next_line8;
        }

        if (str_short[0] == 'P')
        {
            sprintf(str, "CVM:ПИН ВВЕДЕН");
        }
        else if (str_short[0] == 'S')
        {
            sprintf(str, "CVM:SIGNATURE REQUIRED");
        }
        else if (str_short[0] == 'C')
        {
            sprintf(str, "CVM:PIN AND SIGNATURE");
        }
        else
        {
            sprintf(str, "CVM:NoCVM");
        }

        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }
        
    next_line8:
        // 15. TVR
        memset(str, 0, 40);
        memset(str_short, 0, 16);
        sprintf(str, "TVR:");
        tag_size = ttk2_get_field_string(0x95, str_short, 16);
        if (tag_size > 0)
        {
            unsigned char *ptr=(unsigned char *)str_short;
            while (tag_size--)
            {
                sprintf(&str[strlen(str)], "%02x", (unsigned int)*ptr);
                ptr++;
            }
        }
        else
        {
            goto next_line9;
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

    next_line9:        
        // 16. AID
        memset(str, 0, 40);
        memset(str_short, 0, 16);
        sprintf(str, "AID:");
        tag_size = ttk2_get_field_string(0x9F06, str_short, 8);
        if (tag_size > 0)
        {
            unsigned char *ptr=(unsigned char *)str_short;
            while (tag_size--)
            {
                sprintf(&str[strlen(str)], "%02x", (unsigned int)*ptr);
                ptr++;
            }
        }
        else
        {
            goto next_line10;
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

    next_line10:
        // 17. Application Label
        memset(str, 0, 40);
        sprintf(str, "APP:");
        tag_size = ttk2_get_field_string(0x50, &str[strlen(str)], 40 - strlen(str));
        if (tag_size > 0)
        {
            if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
            {
                goto exit_print;
            }
        }
        
        // 18. Transaction Certificate
        memset(str, 0, 40);
        memset(str_short, 0, 16);
        sprintf(str, "TC:");
        tag_size = ttk2_get_field_string(0x9F26, str_short, 16);
        if (tag_size > 0)
        {
            unsigned char *ptr=(unsigned char *)str_short;
            while (tag_size--)
            {
                sprintf(&str[strlen(str)], "%02x", (unsigned int)*ptr);
                ptr++;
            }
            if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
            {
                goto exit_print;
            }
        }
        
        // 19. Retrieval Reference Number
        memset(str, 0, 40);
        sprintf(str, "RRN:");
        tag_size = ttk2_get_field_string(0x98, &str[strlen(str)], 40 - strlen(str));
        if (tag_size < 0)
        {
            goto next_line11;
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

    next_line11:
        // 20. Batch No
        memset(str, 0, 40);
        sprintf(str, "BATCH:");
        tag_size = ttk2_get_field_string(0x99, &str[strlen(str)], 40 - strlen(str));
        if (tag_size <= 0)
        {
            goto next_line12;
        }
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }

    next_line12:
        // 21. ERN, RC
        memset(str, 0, 40);
        sprintf(str, "ERN:");
        tag_size = ttk2_get_field_string(0x83, &str[strlen(str)], 40 - strlen(str));
        if (tag_size < 0)
        {
            goto next_line13;
        }
        sprintf(&str[strlen(str)], " RC:");
        tag_size = ttk2_get_field_string(0x9B, &str[strlen(str)], 40 - strlen(str));
        if (tag_size < 0)
        {
            goto next_line13;
        }
        
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
            goto exit_print;
        }
        
    next_line13:        
        sprintf(str, " ");
        if (FiscPrintString(DEFAULT_PASS, 0x07, (CPU_INT08U*)str, &err) != FISC_OK)
        {
        }

exit_print:
       if (FiscBillCut(DEFAULT_PASS, FISC_HALF_CUT, &err) != FISC_OK)
       {
            FPost();
            return;
       }

       FPost();
   }
}
                          
// инициализация пароля
void InitPass(void)
{
  #if  defined(BOARD_SOLARIUM_VLAD)
  CPU_INT32U en_pass = 0;
  #endif
  CPU_INT32U pass,crc,crct;
  GetData(&PassDesc, &pass, 0, DATA_FLAG_SYSTEM_INDEX);
  GetData(&PassCRCDesc, &crc, 0, DATA_FLAG_SYSTEM_INDEX);
  
  crct = crc16((unsigned char*)&pass, sizeof(CPU_INT32U));

  if (crct != crc)
    { // обнуляем, если crc не сошлась
      pass = DEFAULT_PASSWORD;
      crc = crc16((unsigned char*)&pass, sizeof(CPU_INT32U));
      SetData(&PassDesc, &pass, 0, DATA_FLAG_SYSTEM_INDEX);
      SetData(&PassCRCDesc, &crc, 0, DATA_FLAG_SYSTEM_INDEX);
      #if  defined(BOARD_SOLARIUM_VLAD)
      en_pass = 0;
      SetData(&EnableGlobalPassDesc, &en_pass, 0, DATA_FLAG_SYSTEM_INDEX);
      #endif
    }

  #if  defined(BOARD_SOLARIUM_VLAD)
  GetData(&EnableGlobalPassDesc, &en_pass, 0, DATA_FLAG_SYSTEM_INDEX);
  if (en_pass > 1)
  {
      en_pass = 0;
      SetData(&EnableGlobalPassDesc, &en_pass, 0, DATA_FLAG_SYSTEM_INDEX);
  }
  
  CheckVladPasswords();

  #endif
}
