#include <includes.h>
#include "app_serv.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 "menu.h"
#include "journal.h"
#include "fr.h"
#include "CRC16.h"
#include "host_app.h"
#include "console.h"
#include "keyboard.h"

// если определить этот макрос, будут вноситься деньги по кнопке F1
//#define _DEBUG_MONEY

CPU_INT32U SystemTime;
CPU_INT32U money_timestamp[COUNT_POST + COUNT_VACUUM];

//CPU_INT08U ChannelsState[COUNT_POST + COUNT_VACUUM];
  #define CHANNEL_STATE_FREE            0
  #define CHANNEL_STATE_BUSY            1
  #define CHANNEL_STATE_DISABLED        2
//CPU_INT32U ChannelsCounters[COUNT_POST + COUNT_VACUUM];
CPU_INT32U ChannelsPayedTime[COUNT_POST + COUNT_VACUUM];

#define USER_QUERY_LEN  512

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

int GetUserEvent(int* event);
void UserPrintMoneyMenu(int post);
void WorkServer(void);
void UserPrintThanksMenu(int post);
void UserPrintFirstMenu(void);
void UserPrintErrorMenu(void);
CPU_INT32U GetChannelsTimeForFree(CPU_INT08U ch);

void LoadAcceptedMoney(void);
void SetAcceptedMoney(CPU_INT32U money,int post);
void ClearAcceptedMoney(int post);
CPU_INT32U GetAcceptedMoney(int post);

void SetAcceptedBankMoney(CPU_INT32U money,int post);
void ClearAcceptedBankMoney(int post);
CPU_INT32U GetAcceptedBankMoney(int post);

void InitPass(void);
int CheckChannelEnabled(CPU_INT08U channel);
int ChannelBusy(CPU_INT08U ch);
void UserPrintIpDeviceErrMenu(CPU_INT08U post);
void UserPrintPrintBillMenu(int post);

#ifdef BOARD_CENTRAL_CFG
static char incassation;
static char was_critical_error;
#endif

#ifdef BOARD_POST_CFG

static int out_pulse_count = 0;
static int out_pulse_len = 0;

void AddOutPulses(int count, int len_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;

    OS_EXIT_CRITICAL();
}

#endif

static int currentPosition = 0;

typedef enum {
  
  waitMoney = 0,
  insertMoney,
  washing,
  printCheck
    
} washStateEnum;

static washStateEnum wash_State[COUNT_POST + COUNT_VACUUM] = {waitMoney, waitMoney, waitMoney, waitMoney, waitMoney, waitMoney, waitMoney, waitMoney};

static int countSecWait[COUNT_POST + COUNT_VACUUM] = {0, 0, 0, 0, 0, 0, 0, 0};
static CPU_INT32U enable_coin[COUNT_POST + COUNT_VACUUM];
static CPU_INT32U cash_enable[COUNT_POST];
static CPU_INT32U bank_enable[COUNT_POST];
static CPU_INT32U enable_signal[COUNT_POST];
static CPU_INT32U fiscal_enable;

// тайм аут меню печать чека после снятия ошибки фискальника
static CPU_INT16U time_out_print_check[COUNT_POST + COUNT_VACUUM] = {0, 0, 0, 0, 0, 0, 0, 0};

void DrawMenu(void)
{
  if (GetMode() != MODE_WORK) return;
    
  if((SystemTime%2))
  {
    if(currentPosition >= COUNT_POST + COUNT_VACUUM)
    {
      currentPosition = 0;
    }

    for( ;currentPosition < COUNT_POST + COUNT_VACUUM; currentPosition++)
    {
      if((enable_coin[currentPosition] 
         || cash_enable[currentPosition] 
           || bank_enable[currentPosition]
              || enable_signal[currentPosition])
                && currentPosition < COUNT_POST) break;
      else if((enable_coin[currentPosition])
              && currentPosition >= COUNT_POST) break;
    }
    
    if(currentPosition >= COUNT_POST + COUNT_VACUUM)
    {
      currentPosition = 0;
      return;
    }

    UserPrintMoneyMenu(currentPosition);
    currentPosition++;
  }
}

/*
 Сервер обработки событий пользователя
*/
void UserAppTask(void *p_arg)
{
  CPU_INT32U print_timeout;
  int event;
  
#ifdef BOARD_CENTRAL_CFG
  
  static CPU_INT08U fr_conn_ctr = 0;
    
  incassation = 0;
  was_critical_error = 0;
#endif

  UserPrintFirstMenu();

  int testMoney = 0;

  while (1)
    {
      if (GetUserEvent(&event))
        {
          switch (event){
#ifdef BOARD_CENTRAL_CFG
            case EVENT_UPATE_RTC:
              {
                // прочитаем текущее время
                CPU_INT32U time = GetTimeSec();

                if(SystemTime != time)
                {
                  SystemTime = time;
                  PostUserEvent(EVENT_SEC);
                }
                
                // проверка режима
                CheckMode();

              }
            break;
            case EVENT_SEC:
            {
              CPU_INT32U accmoney = 0;

              // рабочий сервер - счетчики, состояния и т.п.
              WorkServer();

              GetData(&EnableFiscalDesc, &fiscal_enable, 0, DATA_FLAG_SYSTEM_INDEX);

              if(!fiscal_enable)
              {
                  FiscalConnState = FISCAL_NOCONN;
              }

              // проверим фискальник, всегда его проверяем
              if ((++fr_conn_ctr % 5) == 0)
              {
                  if (ConnectFiscalFast() == 0)
                  {
                      CheckFiscalStatus();
                  }
              }

              // сервер ошибок
              ErrorServer();
                
              // дальше только в рабочем режиме
//              if (GetMode() != MODE_WORK)
//              {
//                  break;
//              }

              for(int post = 0; post < COUNT_POST + COUNT_VACUUM; post++)
              {
                GetData(&EnableCoinDesc, &enable_coin[post], post, DATA_FLAG_DIRECT_INDEX);  
                
                if(post < COUNT_POST)
                {
                  GetData(&EnableValidatorDesc, &cash_enable[post], post, DATA_FLAG_DIRECT_INDEX);
                  GetData(&EnableBankDesc, &bank_enable[post], post, DATA_FLAG_DIRECT_INDEX);
                  GetData(&EnableSignalDesc, &enable_signal[post], post, DATA_FLAG_DIRECT_INDEX);
                }

                accmoney = GetAcceptedMoney(post);
                accmoney += GetAcceptedBankMoney(post);

                if (accmoney > 0 && !was_critical_error)
                {
                    CPU_INT32U tick = labs(OSTimeGet() - money_timestamp[post]);

                    // есть деньги и нет ошибок
                    if(wash_State[post] != washing)
                    { 
                      // аварийный тайм аут ожидания начала мойки
                      if (tick > 300000UL)
                      {
                          countSecWait[post] = 0;
                          PostUserEvent(EVENT_CASH_PRINT_CHECK_POST1 + post);
                            
                          // событие послали - к следующему каналу
                          continue;
                      }
                    }
                    else
                    {
                      // в режиме мойки свой тайм аут в зависимости от суммы и цены минуты мойки
                      GetData(&CashPerMinuteDesc, &print_timeout, 0, DATA_FLAG_SYSTEM_INDEX);
                      if(print_timeout)
                      {
                        // если указана стоимость минуты - по исчерпанию времени мойки принудительно запускаем печать чека
                        if (tick > ((60000UL * accmoney) / print_timeout))
                        {
                            countSecWait[post] = 0;
                            PostUserEvent(EVENT_CASH_PRINT_CHECK_POST1 + post);
                              
                            // событие послали - к следующему каналу
                            continue;
                        }
                      }
                      else
                      {
                          // если нет цены минуты - нет и тайм аута мойки
                          money_timestamp[post] = OSTimeGet();
                      }
                    }
                }
                
                if(countSecWait[post])
                {
                  countSecWait[post]--;

                  // пришло время печати чека, но не должно быть ошибок - печатаем чек
                  if(!countSecWait[post] && !was_critical_error) PostUserEvent(EVENT_CASH_PRINT_CHECK_POST1 + post);
                  // пришло время печати чека - ждем деньги
                  else if(!countSecWait[post]) wash_State[post] = waitMoney;
                }
              }

              // монетоприемники для пылесосов без сигнала окончания приема денег - только по тайм ауту
              for(int post = COUNT_POST; post < COUNT_POST + COUNT_VACUUM; post++)
              {
                  accmoney = GetAcceptedMoney(post);
                  
                  if (accmoney > 0 && !was_critical_error)
                  {   // есть деньги и нет ошибок
                      // для монетоприемников пылесосов - печать чека по таймауту
                      GetData(&CoinTimeOutDesc, &print_timeout, post, DATA_FLAG_DIRECT_INDEX);
                   
                      if (labs(OSTimeGet() - money_timestamp[post]) > 1000UL * print_timeout)
                      {
                          PostUserEvent(EVENT_CASH_PRINT_CHECK_POST1 + post);
                      }
                  }
              }

              // принимаем деньги
              DrawMenu();

              // если есть ошибки, не работаем
              if (TstCriticalErrors()) 
              {
                UserPrintErrorMenu(); 
                RefreshMenu();

                // выключим прием денег, деньги принимаются - но чеки не печатаем - копим
                if (was_critical_error == 0)
                {
                    for(int post = 0; post < COUNT_POST + COUNT_VACUUM; post++)
                    {
                          // обнулим переменные, если находились в режиме печати чека или его ожидания
                          // или даже сразу после снятия ошибки фискальника
                          if(wash_State[post] == printCheck)
                          { 
                              wash_State[post] = waitMoney;
                              countSecWait[post] = 0;
                          }
                          
                          time_out_print_check[post] = 0;
                    }

                    was_critical_error = 1;
                }
                break;
              }
              
              // включим заново прием денег, если была ошибка
              if (was_critical_error)
              {
                  was_critical_error = 0;
                  
                  // ошибки кончились - напечатаем чек с суммой, которая накопилась за время отсутсвия бумаги, 
                  // если конечно надо
                  for(int post = 0; post < COUNT_POST + COUNT_VACUUM; post++)
                  {
                      accmoney = GetAcceptedMoney(post);
                      accmoney += GetAcceptedBankMoney(post);
                      
                      // заодно на всякий случай сбросим сообщение о печати чека  и тайм аут его ожидания
                      if(wash_State[post] == printCheck) 
                      { 
                          wash_State[post] = waitMoney;
                          countSecWait[post] = 0;
                      }

                      if (accmoney > 0)
                      {
                         // перезапустим тайм ауты - что бы по тайм ауту не вовремя не сбросить накопленное
                         money_timestamp[post] = OSTimeGet();
                         
                         // взведем таймаут печати чека - послали же запрос
                         time_out_print_check[post] = 35;

                         // после сброса ошибки чеки печатаем не сразу - а через тайм аут печати, вдруг не все исправилось?
                         PostUserEvent(EVENT_WAIT_CASH_PRINT_CHECK_POST1 + post);
                      }
                  }
                  
                  break;
              }
              
              // тайм аут ожидания печати после ошибки фискальника - 35 секунд
              for(int post = 0; post < COUNT_POST + COUNT_VACUUM; post++)
              {
                  if(time_out_print_check[post])
                  {
                      time_out_print_check[post]--;

                      if(!time_out_print_check[post] && wash_State[post] == printCheck)
                      {
                          wash_State[post] = waitMoney;
                          countSecWait[post] = 0;
                      }
                  }
              }
            }
            break;
              
            case EVENT_MODE_CHANGE:
              ReInitMenu();
              SaveEventRecord(0, JOURNAL_EVENT_CHANGE_MODE, GetMode());
              break;

            case EVENT_COIN_INSERTED_POST1:
            case EVENT_COIN_INSERTED_POST2:
            case EVENT_COIN_INSERTED_POST3:
            case EVENT_COIN_INSERTED_POST4:
            case EVENT_COIN_INSERTED_POST5:
            case EVENT_COIN_INSERTED_POST6:
            case EVENT_COIN_INSERTED_VACUUM1:
            case EVENT_COIN_INSERTED_VACUUM2:
              {
                CPU_INT32U cpp = 1;
                CPU_INT32U money, accmoney = 0;
                int number_post = event - EVENT_COIN_INSERTED_POST1;
                
                if(wash_State[number_post] != waitMoney && wash_State[number_post] != insertMoney) break;
                if(!enable_coin[number_post]) break;

                GetData(&CoinPerPulseDesc, &cpp, number_post, DATA_FLAG_DIRECT_INDEX);
                
                money = cpp * GetResetCoinCount(number_post) + testMoney;

                accmoney = GetAcceptedMoney(number_post);
                accmoney += money;

                SetAcceptedMoney(accmoney, number_post);
                money_timestamp[number_post] = OSTimeGet();
                                
                if (money) SaveEventRecord(number_post, JOURNAL_EVENT_MONEY_COIN_POST1 + number_post, money);
                
                wash_State[number_post] = insertMoney;
                testMoney = 0;
              }
              break;
            case EVENT_CASH_INSERTED_POST1:
            case EVENT_CASH_INSERTED_POST2:
            case EVENT_CASH_INSERTED_POST3:
            case EVENT_CASH_INSERTED_POST4:
            case EVENT_CASH_INSERTED_POST5:
            case EVENT_CASH_INSERTED_POST6:
              {
                CPU_INT32U cpp = 1;
                CPU_INT32U money, accmoney = 0;
                int number_post = event - EVENT_CASH_INSERTED_POST1;
                
                if(wash_State[number_post] != waitMoney && wash_State[number_post] != insertMoney) break;
                if(!cash_enable[number_post]) break;

                GetData(&CashPerPulseDesc, &cpp, number_post, DATA_FLAG_DIRECT_INDEX);
                
                money = cpp * GetResetCashCount(number_post) + testMoney;
                
                accmoney = GetAcceptedMoney(number_post);
                accmoney += money;
                SetAcceptedMoney(accmoney, number_post);
                money_timestamp[number_post] = OSTimeGet();
                                
                if (money) SaveEventRecord(number_post, JOURNAL_EVENT_MONEY_NOTE_POST1 + number_post, money);
                
                wash_State[number_post] = insertMoney;
                testMoney = 0;
              }
              break;
             
            case EVENT_BANK_INSERTED_POST1:
            case EVENT_BANK_INSERTED_POST2:
            case EVENT_BANK_INSERTED_POST3:
            case EVENT_BANK_INSERTED_POST4:
            case EVENT_BANK_INSERTED_POST5:
            case EVENT_BANK_INSERTED_POST6:
              {
                CPU_INT32U cpp = 1;
                CPU_INT32U money, accmoney = 0;
                int number_post = event - EVENT_BANK_INSERTED_POST1;
                
                if(wash_State[number_post] != waitMoney && wash_State[number_post] != insertMoney) break;
                if(!bank_enable[number_post]) break;

                GetData(&BankPerPulseDesc, &cpp, number_post, DATA_FLAG_DIRECT_INDEX);
                
                money = cpp * GetResetbankCount(number_post) + testMoney;
                
                accmoney = GetAcceptedBankMoney(number_post);
                accmoney += money;
                SetAcceptedBankMoney(accmoney, number_post);
                money_timestamp[number_post] = OSTimeGet();
                                
                if (money) SaveEventRecord(number_post, JOURNAL_EVENT_MONEY_BANK_POST1 + number_post, money);
                
                wash_State[number_post] = insertMoney;
                testMoney = 0;
              }
              break;

            case EVENT_KEY_CANSEL:
              break;
              
            case EVENT_KEY_START:
            case EVENT_KEY_USER_START:
              if (incassation) break;
              if (GetMode() != MODE_WORK)
                {
                  if (!FlagForPrintReport) break;
                  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();
                      }
                   }
                  else if (GetCurrentMenu() == CanselCheckMenuPanel)
                    {
                      int res = CanselFiscalBill();
                      SaveEventRecord(0, JOURNAL_EVENT_PRINT_X, res);
                      CheckFiscalStatus();
                      GoToPreviousMenu();
                    }
                  break;
                }
            
              if (TstCriticalErrors()) 
              {
                UserPrintErrorMenu(); 
                RefreshMenu(); 
                break;
              }
                  
              break;
            case EVENT_STOP_MONEY_POST1:
            case EVENT_STOP_MONEY_POST2:
            case EVENT_STOP_MONEY_POST3:
            case EVENT_STOP_MONEY_POST4:
            case EVENT_STOP_MONEY_POST5:
            case EVENT_STOP_MONEY_POST6:
            //if (GetMode() == MODE_WORK) //
            {
                int number_post = event - EVENT_STOP_MONEY_POST1;
                CPU_INT32U accmoney = 0;

                accmoney = GetAcceptedMoney(number_post);
                accmoney += GetAcceptedBankMoney(number_post);
                
                if ((accmoney > 0) && (wash_State[number_post] != washing) && (wash_State[number_post] != printCheck))
                {
                    wash_State[number_post] = washing;
                    SaveEventRecord(number_post, JOURNAL_EVENT_WASHING, number_post);
                }
                
                money_timestamp[number_post] = OSTimeGet();
            }
            break;

            case EVENT_WAIT_CASH_PRINT_CHECK_POST1:
            case EVENT_WAIT_CASH_PRINT_CHECK_POST2:
            case EVENT_WAIT_CASH_PRINT_CHECK_POST3:
            case EVENT_WAIT_CASH_PRINT_CHECK_POST4:
            case EVENT_WAIT_CASH_PRINT_CHECK_POST5:
            case EVENT_WAIT_CASH_PRINT_CHECK_POST6:
              //if (GetMode() == MODE_WORK) //
              {
                  int number_post = event - EVENT_WAIT_CASH_PRINT_CHECK_POST1;
                  int count_delay = 0;
                  CPU_INT32U accmoney = 0;

                  accmoney = GetAcceptedMoney(number_post);
                  accmoney += GetAcceptedBankMoney(number_post);

                  if ((accmoney > 0) && (wash_State[number_post] != printCheck) && !was_critical_error)
                  {
                    // запустим задержку печати чека, если нет ошибок
                    GetData(&PrintTimeoutDesc, &count_delay, 0, DATA_FLAG_DIRECT_INDEX);
                    countSecWait[number_post] = count_delay;
  
                    wash_State[number_post] = printCheck;

                    // если задержки нет - сразу печатаем
                    if(countSecWait[number_post] == 0) PostUserEvent(EVENT_CASH_PRINT_CHECK_POST1 + number_post);
                  }
                  else if (was_critical_error)
                  {
                    // если есть ошибки - просто продолжаем ожидать прием денег
                    wash_State[number_post] = waitMoney;
                  }
              }
            break;
              
            case EVENT_CASH_PRINT_CHECK_POST1:
            case EVENT_CASH_PRINT_CHECK_POST2:
            case EVENT_CASH_PRINT_CHECK_POST3:
            case EVENT_CASH_PRINT_CHECK_POST4:
            case EVENT_CASH_PRINT_CHECK_POST5:
            case EVENT_CASH_PRINT_CHECK_POST6:
            case EVENT_CASH_PRINT_CHECK_VACUUM1:
            case EVENT_CASH_PRINT_CHECK_VACUUM2:

            //if (GetMode() == MODE_WORK) // прием денег идет всегда
            {
              int number_post = event - EVENT_CASH_PRINT_CHECK_POST1;
              CPU_INT32U accmoney = 0;

              // переходим в ожидание приема денег
              wash_State[number_post] = waitMoney;
              // сбросим таймаут ожидания печати чека после снятия ошибки
              time_out_print_check[number_post] = 0;

              if (was_critical_error)
              {
                  break;
              }
              
              // здесь событие старта печати чека - включили насос или пылесос
              accmoney = GetAcceptedMoney(number_post);
              
              if (accmoney > 0)
              { 
                  UserPrintPrintBillMenu(number_post);
                  RefreshMenu();
                  
                  // напечатаем наличный чек
                  if (IsFiscalConnected())
                  {
                    if (PrintFiscalBill(accmoney, number_post, 0) == 0) // здесь добавить с какого поста чек
                    {
                        SaveEventRecord(number_post, JOURNAL_EVENT_PRINT_BILL_POST1 + number_post, accmoney);
                    }
                  }

                  // если есть ошибки, не работаем - полученные деньги не обнуляем
                  if (TstCriticalErrors())
                  {
                    // выключим прием денег
                    if (was_critical_error == 0) {was_critical_error = 1;}
                    break;
                  }

                  IncCounter(number_post, ChannelsPayedTime[number_post], accmoney);
                  SetAcceptedMoney(0, number_post);
                  if (GetMode() == MODE_WORK) OSTimeDly(1000);
                 
                  // повесим меню "СПАСИБО"                      
                  if (IsFiscalConnected())
                  {
                      UserPrintThanksMenu(number_post);
                      RefreshMenu();
                  }
                  
                  if (GetMode() == MODE_WORK) OSTimeDly(1000);
              }
              
              // здесь событие старта печати чека - включили насос или пылесос
              accmoney = GetAcceptedBankMoney(number_post);
              
              if (accmoney > 0)
              { 
                  UserPrintPrintBillMenu(number_post);
                  RefreshMenu();
                  
                  // напечатаем безналичный чек
                  if (IsFiscalConnected())
                  {
                    if (PrintFiscalBill(accmoney,number_post, 1) == 0) // здесь добавить с какого поста чек
                    {
                        SaveEventRecord(number_post, JOURNAL_EVENT_PRINT_BILL_ONLINE_POST1 + number_post, accmoney);
                    }
                  }

                  // если есть ошибки, не работаем - полученные деньги не обнуляем
                  if (TstCriticalErrors()) 
                  {    
                    // выключим прием денег
                    if (was_critical_error == 0) {was_critical_error = 1;}
                    break;
                  }

                  IncCounter(number_post, ChannelsPayedTime[number_post], accmoney);
                  SetAcceptedBankMoney(0, number_post);
                  if (GetMode() == MODE_WORK) OSTimeDly(1000);
                 
                  // повесим меню "СПАСИБО"                      
                  if (IsFiscalConnected())
                  {
                      UserPrintThanksMenu(number_post);
                      RefreshMenu();
                  }
                  
                  if (GetMode() == MODE_WORK) OSTimeDly(1000);
              }
            }
            break;

            case EVENT_KEY_F1:
//                testMoney = 100;
//                PostUserEvent(EVENT_COIN_INSERTED_POST1);
//                PostUserEvent(EVENT_COIN_INSERTED_POST2);
//                PostUserEvent(EVENT_COIN_INSERTED_POST3);
//                PostUserEvent(EVENT_COIN_INSERTED_POST4);
//                PostUserEvent(EVENT_COIN_INSERTED_POST5);
//                PostUserEvent(EVENT_COIN_INSERTED_POST6);
//                PostUserEvent(EVENT_COIN_INSERTED_VACUUM1);
//                PostUserEvent(EVENT_COIN_INSERTED_VACUUM2);

                /*FIO4SET_bit.P4_28 = 1;
                OSTimeDly(50);
                FIO4CLR_bit.P4_28 = 1; 
                OSTimeDly(50);
                FIO4SET_bit.P4_28 = 1;
                OSTimeDly(50);
                FIO4CLR_bit.P4_28 = 1; 
                OSTimeDly(50);
                FIO4SET_bit.P4_28 = 1;
                OSTimeDly(50);
                FIO4CLR_bit.P4_28 = 1;
                OSTimeDly(50);
                FIO4SET_bit.P4_28 = 1;
                OSTimeDly(50);
                FIO4CLR_bit.P4_28 = 1; 
                OSTimeDly(50);
                FIO4SET_bit.P4_28 = 1;
                OSTimeDly(50);
                FIO4CLR_bit.P4_28 = 1;*/
            break;
            case EVENT_KEY_F2:
                testMoney = 0;
//                PostUserEvent(EVENT_STOP_MONEY_POST1);
//                PostUserEvent(EVENT_STOP_MONEY_POST2);
//                PostUserEvent(EVENT_STOP_MONEY_POST3);
//                PostUserEvent(EVENT_STOP_MONEY_POST4);
//                PostUserEvent(EVENT_STOP_MONEY_POST5);
//                PostUserEvent(EVENT_STOP_MONEY_POST6);
            break;
            case EVENT_KEY_F3:
                //testMoney = 100;
//                PostUserEvent(EVENT_WAIT_CASH_PRINT_CHECK_POST1);
//                PostUserEvent(EVENT_WAIT_CASH_PRINT_CHECK_POST2);
//                PostUserEvent(EVENT_WAIT_CASH_PRINT_CHECK_POST3);
//                PostUserEvent(EVENT_WAIT_CASH_PRINT_CHECK_POST4);
//                PostUserEvent(EVENT_WAIT_CASH_PRINT_CHECK_POST5);
//                PostUserEvent(EVENT_WAIT_CASH_PRINT_CHECK_POST6);
            break;
#endif
            default:
              break;
          }
        }
      else 
        {
          OSTimeDly(1);
        }
    }
}

/*!
 Пользовательская инициализация
*/
void UserStartupFunc(void)
{
#ifdef BOARD_CENTRAL_CFG
  // инициализация режима работы
  InitMode();
    
  // инициализация данных
  CheckAllData();
    
  //OnChangeInitByDefault();
      
  // проверим длинные счетчики
  CheckLongCounters();
      
  // восстановим деньги
  LoadAcceptedMoney();
  
  // проверим пароль
  InitPass();
    
  // инициализация меню
  InitMenu();

  OSTimeDly(1000);
  
  InitFiscal();

  // проинициализируем часы
  InitRTC();

  // сделаем запись о включении
  SaveEventRecord(0, JOURNAL_EVENT_DEVICE_ON, GetTimeSec());

  // запустим монетник
  InitCoin();

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

  //InitConsole();
  
#ifdef BOARD_CENTRAL_CFG
  //InitHostApp();
#endif
      
  SystemTime = GetTimeSec();
  
#ifdef BOARD_CENTRAL_CFG
  // перейдем в стартовое меню, если работа
  if (GetMode() == MODE_WORK) {SetMenu(WORK_MENU);}
  else SetMenu(SERVICE_MENU);
#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)
{
}

void UserPrintMoneyMenu(int post)
{
    char buf[32];
    CPU_INT32U accmoney;

    if (GetMode() != MODE_WORK) return;
      
    strcpy(buf, " ");
    PrintUserMenuStr(buf, 0);

    accmoney = GetAcceptedMoney(post) + GetAcceptedBankMoney(post);

    if((wash_State[post] == waitMoney) && (accmoney > 0))
    {
        // если была перезагрузка и есть деньги - сразу переходим на ожидание денег
        wash_State[post] = insertMoney;
    }

    if(wash_State[post] == waitMoney)
    {
      sprintf(buf, " Внесите деньги");
      PrintUserMenuStr(buf, 1);

      sprintf(buf, " ");
      PrintUserMenuStr(buf, 2);
    }
    else if(wash_State[post] == insertMoney)
    {
      sprintf(buf, " Внесите деньги");
      PrintUserMenuStr(buf, 1);

      sprintf(buf, "Принято %d руб.", accmoney);
      PrintUserMenuStr(buf, 2);
    }
    else if(wash_State[post] == washing)
    {
      sprintf(buf, "Началась мойка");
      PrintUserMenuStr(buf, 1);

      sprintf(buf, " ");
      PrintUserMenuStr(buf, 2);
    }
    else if(wash_State[post] == printCheck)
    {
      sprintf(buf, "Ожидание печати");
      PrintUserMenuStr(buf, 1);

      sprintf(buf, " ");
      PrintUserMenuStr(buf, 2);
    }

    if(post < COUNT_POST)
      sprintf(buf, " Пост %d", post + 1);
    else if(post < COUNT_POST + COUNT_VACUUM)
      sprintf(buf, "Пылесос %d", post + 1 - COUNT_POST);
    else 
      sprintf(buf, " ");

    PrintUserMenuStr(buf, 3);
}
                         
// вывод меню о невозможности работы
void UserPrintErrorMenu(void)
{
  char buf[32];
  
  if (GetMode() != MODE_WORK) return;
    
  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);
    }
}

void WorkServer(void)
{

}

void UserPrintPrintBillMenu(int post)
{
  char buf[32];
  
  if (GetMode() != MODE_WORK) return;
    
  sprintf(buf, " ");
  PrintUserMenuStr(buf, 0);
  sprintf(buf, "Идeт печать");
  PrintUserMenuStr(buf, 1);
  sprintf(buf, "   чека");
  PrintUserMenuStr(buf, 2);
  
  if(post < COUNT_POST)
    sprintf(buf, " Пост %d", post + 1);
  else 
    sprintf(buf, "Пылесос %d", post + 1 - COUNT_POST);

  PrintUserMenuStr(buf, 3);
}

void UserPrintThanksMenu(int post)
{
  char buf[32];
  
  if (GetMode() != MODE_WORK) return;
    
  sprintf(buf, " ");
  PrintUserMenuStr(buf, 0);
  sprintf(buf, "   СПАСИБО");
  PrintUserMenuStr(buf, 1);
  sprintf(buf, " ");
  PrintUserMenuStr(buf, 2);
  
  if(post < COUNT_POST)
    sprintf(buf, " Пост %d", post + 1);
  else 
    sprintf(buf, "Пылесос %d", post + 1 - COUNT_POST);

  PrintUserMenuStr(buf, 3);
}


int ChannelBusy(CPU_INT08U ch)
{
    return 0;
}
            
void UserPrintFirstMenu(void)
{
    char buf[32];
    
    if (GetMode() != MODE_WORK) return;

    sprintf(buf, " ");
    PrintUserMenuStr(buf, 0);
    sprintf(buf, "    Все посты");
    PrintUserMenuStr(buf, 1);
    sprintf(buf, "    выключены");
    PrintUserMenuStr(buf, 2);
    sprintf(buf, " ");
    PrintUserMenuStr(buf, 3);
}


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

  for(int i = 0; i < COUNT_POST + COUNT_VACUUM; i++)
  {
    // считаем cохраненные деньги из FRAM
    GetData(&AcceptedMoneyDesc, &m, i, DATA_FLAG_DIRECT_INDEX);    
    // считаем crc16 этих денег из FRAM 
    GetData(&AcceptedMoneyCRC16Desc, &crc, i, DATA_FLAG_DIRECT_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, i, DATA_FLAG_DIRECT_INDEX);
        SetData(&AcceptedMoneyCRC16Desc, &crc, i, DATA_FLAG_DIRECT_INDEX);
      }
  }
  
  for(int i = 0; i < COUNT_POST; i++)
  {
    // считаем cохраненные деньги из FRAM
    GetData(&AcceptedBankMoneyDesc, &m, i, DATA_FLAG_DIRECT_INDEX);    
    // считаем crc16 этих денег из FRAM 
    GetData(&AcceptedBankMoneyCRC16Desc, &crc, i, DATA_FLAG_DIRECT_INDEX);    
    
    crct = crc16((unsigned char*)&m, sizeof(CPU_INT32U));
  
    if (crct != crc)
      { // обнуляем, если crc не сошлась
        m = 0;
        crc = crc16((unsigned char*)&m, sizeof(CPU_INT32U));
        SetData(&AcceptedBankMoneyDesc, &m, i, DATA_FLAG_DIRECT_INDEX);
        SetData(&AcceptedBankMoneyCRC16Desc, &crc, i, DATA_FLAG_DIRECT_INDEX);
      }
  }
}

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

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

// очистить счетчик денег
CPU_INT32U GetAcceptedMoney(int post)
{
  CPU_INT32U m;
  GetData(&AcceptedMoneyDesc, &m, post, DATA_FLAG_DIRECT_INDEX);
  return m;
}

// добавить денег
void SetAcceptedBankMoney(CPU_INT32U money, int post)
{
  CPU_INT32U m,crc;
  
  if(post >= COUNT_POST) return;

  m=money;
  crc = crc16((unsigned char*)&m, sizeof(CPU_INT32U));
  SetData(&AcceptedBankMoneyDesc, &m, post, DATA_FLAG_DIRECT_INDEX);
  SetData(&AcceptedBankMoneyCRC16Desc, &crc, post, DATA_FLAG_DIRECT_INDEX);
}

// очистить счетчик денег
void ClearAcceptedBankMoney(int post)
{
  CPU_INT32U m,crc;
  
  if(post >= COUNT_POST) return;

  m=0;
  crc = crc16((unsigned char*)&m, sizeof(CPU_INT32U));
  SetData(&AcceptedBankMoneyDesc, &m, post, DATA_FLAG_DIRECT_INDEX);
  SetData(&AcceptedBankMoneyCRC16Desc, &crc, post, DATA_FLAG_DIRECT_INDEX);
}

// очистить счетчик денег
CPU_INT32U GetAcceptedBankMoney(int post)
{
  CPU_INT32U m;

  if(post >= COUNT_POST) return 0;

  GetData(&AcceptedBankMoneyDesc, &m, post, DATA_FLAG_DIRECT_INDEX);
  return m;
}

// инициализация пароля
void InitPass(void)
{
  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);
    }
}
