102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
1

Расчет контрольной суммы для bluetooth ККМ CMP-10

22.10.2011, 08:41. Показов 3679. Ответов 24
Метки нет (Все метки)

Коллеги,

помогите пожалуйста с расчетом контрольной суммы ККМ CMP-10

Дословно:
контрольная сумма – это два младших байта суммы байтов сообщения
(суммированию подлежит часть сообщения, начиная с первого байта за преамбулой
( SOH ) вплоть до постамбулы (ENQ), включая ее).
Диапазон значений: 0000h-FFFFh.
длина: 4 байта
каждая цифра контрольной суммы передается в виде символа, код которого
равен значению цифры плюс 30h.
Например, сумма 1AE3h передается как последовательность символов с кодами
31h, 3Ah, 3Eh, 33h.
Ума не приложу, как именно сделать, уже были попытки, но они ни к чему не привели
Есть также вопросы по поводу "суммирования подлежит части сообщения" - это как? Перевести все байты в INT а потом + + + + ..., так что ли?...
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
22.10.2011, 08:41
Ответы с готовыми решениями:

Расчет контрольной суммы файла
Доброго времени, форумчане. Подскажите, пожалуйста, примеры расчета контрольной суммы файла....

Расчет контрольной суммы
Правильно ли я понимаю задание? Настройки com порта стандартные: 9600, 1 стартовый бит, 8 бит...

Код по алгоритму. Расчет контрольной суммы
Помогите написать код по этому алгоритму. 1) Пользователь может выбрать ввод 8-ми или на...

Структура пакета и расчет контрольной суммы для RS485
Стоит задача реализовать структуру (скелет) пакета для обмена по RS485. Master-орм выступает...

24
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
22.10.2011, 11:18 2
Цитата Сообщение от SNOOPYKZ Посмотреть сообщение
Перевести все байты в INT а потом + + + + ..., так что ли?...
Ну да.
Только можно и не переводить - они при суммировании сами переведутся.
0
102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
22.10.2011, 11:52  [ТС] 3
Ответ найден. Подсмотрел у производителя декомпилировав их софт.
0
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
22.10.2011, 11:58 4
Было бы интересно глянуть реализацию
0
102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
22.10.2011, 13:00  [ТС] 5
Не полный класс, но самая важная функция - формирования пакета сделана и оттестирована
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.IO.Ports;
 
namespace AtlasActiveX
{
    public interface IAtlasCMP10
    {
        Exception SendMessage(int seq, string pwd, byte cmd, byte[] data);
    }
 
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class AtlasCMP10 : IAtlasCMP10
    {
        /// <summary>
        /// Преамбула (открывающая скобка сообщения)
        /// </summary>
        public byte SOH { get { return 0x01; } }
        /// <summary>
        /// Разделитель
        /// </summary>
        public byte EOT { get { return 0x04; } }
        /// <summary>
        /// Постамбула (закрывающая скобка сообщения)
        /// </summary>
        public byte ENQ { get { return 0x05; } }
        /// <summary>
        /// Терминатор (завершающий байт)
        /// </summary>
        public byte ETX { get { return 0x03; } }
        /// <summary>
        /// Разделитель аргументов команды
        /// </summary>
        public byte SEP { get { return 0x3B; } }
 
        private int portNo = 1;
        /// <summary>
        /// Номер порта, по-умолчанию = 1
        /// </summary>
        public int PortNo { get { return this.portNo; } set { this.portNo = value > 0 ? value : portNo; } }
 
        private int portBaudRate = 9600;
        /// <summary>
        /// Скорость порта, по-умолчанию = 9600
        /// </summary>
        public int PortBaudRate { get { return this.portBaudRate; } set { this.portBaudRate = value > 0 ? value : portBaudRate; } }
        
        /// <summary>
        /// Порт
        /// </summary>
        protected SerialPort Port { get; set; }
 
        /// <summary>
        /// Метод открывания/создания порта
        /// </summary>
        /// <returns>Ошибка, если была</returns>
        public Exception OpenPort()
        {
            try
            {
                if (this.Port != null) throw new Exception("Порт уже открыт/создан");
 
                this.Port = new SerialPort(String.Format("COM{0}", PortNo), PortBaudRate);
                this.Port.Open();
            }
            catch (Exception ex)
            {
                this.Port = null;
                return ex;
            }
            return null;
        }
 
        /// <summary>
        /// Метод отключения/закрывания порта
        /// </summary>
        /// <returns>Ошибка, если была</returns>
        public Exception ClosePort()
        {
            try
            {
                if (this.Port == null) throw new Exception("Порт уже закрыт/не создан");
                this.Port.Close();
                this.Port = null;
            }
            catch (Exception ex)
            {
                return ex;
            }
            return null;
        }
 
        /// <summary>
        /// Функция расчета байтов контрольной суммы
        /// Диапазон значений: 0000h-FFFFh
        /// Длина: 4 байта
        /// </summary>
        /// <remarks>
        /// Контрольная сумма – это два младших байта суммы байтов сообщения (суммированию подлежит часть сообщения, начиная с первого байта за преамбулой ( SOH ) вплоть до постамбулы (ENQ), включая ее). Каждая цифра контрольной суммы передается в виде символа, код которого равен значению цифры плюс 30h.
        /// </remarks>
        /// <example>
        /// Например, сумма 1AE3h передается как последовательность символов с кодами 31h, 3Ah, 3Eh, 33h.
        /// </example>
        /// <param name="message">byte[]</param>
        /// <returns>byte[]</returns>
        public byte[] CalcMessageBcc(byte[] message)
        {
            List<byte> bcc = new List<byte>();
            int summ = 0;
            foreach (byte b in message) summ += b;
 
            for (int i = 0; i < 4; i++)
            {
                bcc.Add((byte)((summ % 0x10) + 0x30));
                summ /= 0x10;
            }
 
            bcc.Reverse();
            return bcc.ToArray();
        }
 
        /// <summary>
        /// Функция расчета байта порядкого номера сообщения
        /// Диапазон значений: 20h-7Fh
        /// Длина: 1 байт
        /// </summary>
        /// <remarks>
        /// Фискальный регистратор записывает полученный SEQ в ответное сообщение.
        /// </remarks>
        /// <param name="seq">int</param>
        /// <returns>byte</returns>
        public byte CalcSeq(int seq)
        {
            return (byte)((seq % 0x60) + 0x20);
        }
 
        /// <summary>
        /// Функция расчета байта размера сообщения
        /// </summary>
        /// <remarks>
        /// Равняется количеству байтов, находящихся между SOH (0x01) и <bcc> плюс 20h
        /// </remarks>
        /// <param name="data">byte[]</param>
        /// <returns>byte</returns>
        public byte CalcLen(byte[] data)
        {
            int len = 0;
            len += 1; // + 1 байт размера сообщения
            len += 1; // + 1 байт порядковый номер команды
            len += 1; // + 1 байт команда
            len += 6; // + 6 байт пароля
            len += 1; // + 1 байт ограничителя параметров SEP (0x3B) после пароля
            len += data.Length;
            len += 1; // + 1 байт ограничителя параметров SEP (0x3B) после сообщения
            len += 1; // + 1 байт постамбула (закрывающая скобка сообщения)
 
            byte b = (byte)len;
            b += 0x20;
            return b;
        }
 
        /// <summary>
        /// Конструктор команды
        /// </summary>
        /// <param name="seq">Порядковый номер команды</param>
        /// <param name="pwd">Пароль</param>
        /// <param name="cmd">Команда</param>
        /// <param name="data">Параметры</param>
        /// <returns></returns>
        public byte[] BuildMessage(int seq, string pwd, byte cmd, string data)
        {
            return BuildMessage(seq, Encoding.ASCII.GetBytes(pwd), cmd, Encoding.ASCII.GetBytes(data));
        }
 
        /// <summary>
        /// Конструктор команды
        /// </summary>
        /// <param name="seq">Порядковый номер команды</param>
        /// <param name="pwd">Пароль</param>
        /// <param name="cmd">Команда</param>
        /// <param name="data">Параметры</param>
        /// <returns></returns>
        public byte[] BuildMessage(int seq, string pwd, byte cmd, byte[] data)
        {
            return BuildMessage(seq, Encoding.ASCII.GetBytes(pwd), cmd, data);
        }
 
        /// <summary>
        /// Конструктор команды
        /// </summary>
        /// <param name="seq">Порядковый номер команды</param>
        /// <param name="pwd">Пароль</param>
        /// <param name="cmd">Команда</param>
        /// <param name="data">Параметры</param>
        /// <returns></returns>
        public byte[] BuildMessage(int seq, byte[] pwd, byte cmd, byte[] data)
        {
            List<byte> message = new List<byte>();
            message.Add(CalcLen(data));
            message.Add(CalcSeq(seq));
            message.Add(cmd);
 
            message.AddRange(pwd);
            message.Add(this.SEP);
            message.AddRange(data);
            message.Add(this.SEP);
            message.Add(this.ENQ);
 
            //foreach (byte b in message) Console.Write(b.ToString("X") + " ");
            //Console.WriteLine();
 
            byte[] bcc = CalcMessageBcc(message.ToArray()); // контрольная сумма
            message.AddRange(bcc);
            //foreach (byte b in bcc) Console.Write(b.ToString("X") + " ");
            //Console.WriteLine();
 
            List<byte> container = new List<byte>();
            container.Add(this.SOH);
            container.AddRange(message);
            container.Add(this.ETX);
 
            return container.ToArray();
        }
 
        public Exception SendMessage(int seq, string pwd, byte cmd, byte[] data)
        {
            try
            {
                byte[] message = BuildMessage(seq, pwd, cmd, data);
                this.Port.Write(message, 0, message.Length);
            }
            catch (Exception ex)
            { 
            }
 
            return null;
        }
    }
}
1
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
22.10.2011, 13:09 6
Интересно, спасибо.
А есть где-нибудь полная спецификация протокола?

Имеется одно серьезное замечание:
C#
1
2
Exception OpenPort()
Exception ClosePort()
Так делать нельзя.
Исключения существуют для того, чтоб их бросали и ловили, а не возвращали и проверяли.
В вашем случае теряется самая важная часть исключения - Stack Trace.
0
102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
22.10.2011, 13:15  [ТС] 7
Добавлено через 1 минуту
Цитата Сообщение от kolorotur Посмотреть сообщение
Интересно, спасибо.
А есть где-нибудь полная спецификация протокола?
документация

Цитата Сообщение от kolorotur Посмотреть сообщение
Интересно, спасибо.
Имеется одно серьезное замечание:
C#
1
2
Exception OpenPort()
Exception ClosePort()
Так делать нельзя.
Исключения существуют для того, чтоб их бросали и ловили, а не возвращали и проверяли.
В вашем случае теряется самая важная часть исключения - Stack Trace.
Согласен, но не в ActiveX ибо многие системы работаюшие с ActiveX просто не смогут принять Exception. Их система может вылететь еще задолго до их Handler-a
Есть вариант разбить данные методы еще раз, Safe и NonSafe
1
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
22.10.2011, 13:18 8
Цитата Сообщение от SNOOPYKZ Посмотреть сообщение
не в ActiveX
А, у вас ActiveX. Не глянул на название пространства.
С активэксом не работал, потому не знаю что там как.

А что за протокол реализуете? Где почитать можно?
Серийные протоколы - мой маленький фетиш
0
102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
22.10.2011, 13:23  [ТС] 9
Цитата Сообщение от kolorotur Посмотреть сообщение
А, у вас ActiveX. Не глянул на название пространства.
С активэксом не работал, потому не знаю что там как.
Все когда-то бывает в первый раз

Цитата Сообщение от kolorotur Посмотреть сообщение
А что за протокол реализуете? Где почитать можно?
Серийные протоколы - мой маленький фетиш
см. мой предыдущий пост, там есть ссылка на доку и прочую инфу.
0
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
22.10.2011, 13:35 10
Цитата Сообщение от SNOOPYKZ Посмотреть сообщение
см. мой предыдущий пост, там есть ссылка на доку и прочую инфу.
Тьфу ты, не заметил.
Спасибо, почитаем!
0
102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
23.10.2011, 21:15  [ТС] 11
Что-то не могу сообразить, как написать обратную формулу расчета SEQ, если "прямая" выглядит следующим образом
C#
1
(byte)((Seq % 0x60) + 0x20)
Пока сделал "по тупому"

C#
1
int seq = (int)(seq_byte - 0x20)
0
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
23.10.2011, 22:35 12
SNOOPYKZ, да все правильно, в общем-то.
По протоколу значение SEQ болтается в промежутке от 32 до 127, однако в своем приложении наверняка будет проще просто увеличивать значение на единицу, особенно если вы пишете лог всех сообщений. Та функция именно это и делает - "ужимает" любое целое число в промежуток, разрешенный протоколом.
То есть 1 становится 33, а 1285 становится 69.
При получении ответной команды с кодом 69 невозможно будет узнать какое число было изначально туда "ужато" - 1285 или 1381, например.
Так что ваш вариант правильный.
1
102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
23.10.2011, 22:56  [ТС] 13
Цитата Сообщение от kolorotur Посмотреть сообщение
SNOOPYKZ, да все правильно, в общем-то.
По протоколу значение SEQ болтается в промежутке от 32 до 127, однако в своем приложении наверняка будет проще просто увеличивать значение на единицу, особенно если вы пишете лог всех сообщений. Та функция именно это и делает - "ужимает" любое целое число в промежуток, разрешенный протоколом.
То есть 1 становится 33, а 1285 становится 69.
При получении ответной команды с кодом 69 невозможно будет узнать какое число было изначально туда "ужато" - 1285 или 1381, например.
Так что ваш вариант правильный.
Получается, если "сжатие" идет только "в одну сторону", то хранить в логах лучше байты, чем "нормальную" цифру, ибо она будет получатся "не уникальной"
0
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
23.10.2011, 23:04 14
Цитата Сообщение от SNOOPYKZ Посмотреть сообщение
Получается, если "сжатие" идет только "в одну сторону", то хранить в логах лучше байты, чем "нормальную" цифру, ибо она будет получатся "не уникальной"
Тут все зависит от ваших нужд, предложенный в коде метод просто учитывает возможность того, что на своей стороне вы будете держать уникальный номер для каждого сообщения.

Лично я бы делал именно так: постоянно увеличивал счетчик команд, особенно если нужно их писать в лог. В этом случае по-настоящему уникальна каждая команда, а не каждая 128-я.
А хранить в логал лучше всего и то и другое, например в таком формате:
Код
2011-10-23 19:32:23.5323 >> 1285: 01 37 38 20 30 30 30 30 30 30 3B 32 38 30 34 30 37 3B 31 31 34 31 3B 05 30 34 36 31 03
Тут сразу видно и уникальный номер команды и порядковый номер по протоколу (0х38)
1
102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
23.10.2011, 23:25  [ТС] 15
BitArray - возвращает массив бит, если ему дать на вход один байт, нумерация битов будет слева на право или на оборот?
А то в доке к ККМ есть табличка расшифровки битов ошибок и она у них идет от 7-0, а не 0-7..., вот я и задумался...
C#
1
BitArray arr = new BitArray(m2.Status); // где m2.Status, это byte[6]
Даст мне массив из true/false = m2.Status.Length * 8

В тоже время, в доке сказано такое, расшифровка первого байта
7 Всегда равен 1
6 Если установлен – Прочие ошибки оборудования
5 Если установлен - для вступления в силу изменений настроек необходимо перегрузить
ЭККР
4 Если установлен – механизм печатающего устройства не готов к печати (детали Байт S1)
3 Если установлен – дисплей не подключен
2 Если установлен – Аппарат в аварийном режиме
1 Если установлен – буфер документа близок к концу (осталось места на 100 чеков*)
0 Если установлен – регистратор находится в сервисном режиме

Правильно ли я понимаю, что arr[0] - это ВОСЬМОЙ бит, а не первый?
0
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
23.10.2011, 23:33 16
arr[0] - это будет первый (младший) бит.
arr[7], соответственно, - восьмой, старший.

Но я предпочитаю пользоваться обычными битовыми операторами - быстрее, чем создавать класс каждый раз. Особенно если в протоколе ограничение по времени на задержку между сообщениями.
0
102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
23.10.2011, 23:41  [ТС] 17
Цитата Сообщение от kolorotur Посмотреть сообщение
arr[0] - это будет первый (младший) бит.
arr[7], соответственно, - восьмой, старший.
ok

Цитата Сообщение от kolorotur Посмотреть сообщение
Но я предпочитаю пользоваться обычными битовыми операторами - быстрее, чем создавать класс каждый раз. Особенно если в протоколе ограничение по времени на задержку между сообщениями.
Например?
0
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
24.10.2011, 00:10 18
Цитата Сообщение от SNOOPYKZ Посмотреть сообщение
Например?
Например, из вашего:
7 Всегда равен 1
6 Если установлен – Прочие ошибки оборудования
5 Если установлен - для вступления в силу изменений настроек необходимо перегрузить
ЭККР
4 Если установлен – механизм печатающего устройства не готов к печати (детали Байт S1)
3 Если установлен – дисплей не подключен
2 Если установлен – Аппарат в аварийном режиме
1 Если установлен – буфер документа близок к концу (осталось места на 100 чеков*)
0 Если установлен – регистратор находится в сервисном режиме
C#
1
2
3
4
5
6
7
8
byte status; // Пришел от девайса
if (status & 0x81 != 0) Console.WriteLine("регистратор находится в сервисном режиме");
if (status & 0x82 != 0) Console.WriteLine("буфер документа близок к концу (осталось места на 100 чеков*)");
if (status & 0x84 != 0) Console.WriteLine("Аппарат в аварийном режиме");
if (status & 0x88 != 0) Console.WriteLine("дисплей не подключен");
if (status & 0x90 != 0) Console.WriteLine("механизм печатающего устройства не готов к печати (детали Байт S1)");
if (status & 0xA0 != 0) Console.WriteLine("для вступления в силу изменений настроек необходимо перегрузить ЭККР");
if (status & 0xC0 != 0) Console.WriteLine("Прочие ошибки оборудования");
Можно конечно немного "пригладить", создав соответствующий enum и используя его, тогда код будет более читабелен и не понадобится комментировать каждую строчку:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public enum GeneralStatus : byte
{
    None = 0x80,
    InServiceMode = 0x81,
    BufferAlmostFull = 0x82,
    DateNotSet = 0x84,
    DisplayNotConnected = 0x88,
    FaultState = 0x90,
    ReloadRequred = 0xA0,
    Undefined = 0xC0
}
 
void Foo()
{
   byte tmp = 0x9A; // Пришло от девайса
   GeneralStatus status = (GeneralStatus)tmp;
 
   if ((status & GeneralStatus.BufferAlmostFull) != 0) Console.WriteLine("буфер документа близок к концу (осталось места на 100 чеков*)");
   if ((status & GeneralStatus.InServiceMode) != 0) Console.WriteLine("регистратор находится в сервисном режиме");
   // И так далее.
}
Получаем более читабельный и самоописывающийся код с эффективностью побитовых операторов.

По-моему, последний вариант будет читаться лучше, чем
C#
1
if (bitArray[3]) /* do something */
так как не придется читать код с мануалом, чтобы подглядывать что же там этот четвертый бит значит

Добавлено через 4 минуты
Кстати, еще один огромный плюс делать через enum - составление команды:
C#
1
2
GeneralStatus status = GeneralStatus.FaultState | GeneralStatus.DateNotSet;
byte cmd = (byte)status; // Всё, можно отсылать.
Попробуйте проделать то же самое с использованием BitArray
1
102 / 102 / 13
Регистрация: 22.10.2011
Сообщений: 328
24.10.2011, 00:11  [ТС] 19
Давай зачетку, 5+ поставлю
0
Эксперт .NET
16739 / 12492 / 3283
Регистрация: 17.09.2011
Сообщений: 20,719
24.10.2011, 00:13 20
Цитата Сообщение от SNOOPYKZ Посмотреть сообщение
Давай зачетку, 5+ поставлю
Я уже свое отзачитывался, хватит с меня
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
24.10.2011, 00:13
Помогаю со студенческими работами здесь

Расчет контрольной суммы файла
Доброе время суток! не когда не работал с контрольными суммами, не подскажите библиотеку или...

Расчет контрольной суммы ip заголовка
Всем доброго времени суток. Стоит задача рассчитать контрольную сумму ip заголовка. Пробовал...

Расчет контрольной суммы UDP
Пытаюсь связать микроконтроллер с компом по Ethernet'у. Написал ARP протокол, добрался до UDP....

Расчет Контрольной суммы CRC части файла
имеем файл OSC_13.nbf первые 42 байта - это заголовок, где описано, для какого устройства, какая...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2023, CyberForum.ru