3 Commits

Author SHA1 Message Date
ee79c7a376 Ускорить обратную связь R0–R3 задатчика для ПМ67
Перенести регистры индикации VPU в быстрый поток CAN, чтобы R3 не ожидал медленного обхода параметров. Сохранить управление светодиодами и настройки периодов EEPROM.

Добавить проверку настоящих масок и планировщика: интервал R3 составляет 120 мс при быстром делителе 2 и 600 мс при делителе 10. Сохранить изменённый исходник в UTF-8.
2026-10-16 16:46:15 +03:00
9ddea2d99a Исправить восстановление CAN после тайм-аута передачи в 166
Перенесено из коммита 985804607d40b84f985eb04fc1f1b0a0dcfbe795 ветки 167.
Конфликты разрешены с сохранением обработчиков проекта 166.
2026-10-13 19:19:57 +03:00
b25e026dec Перенести исправления строба индикации из 167
Перенести настройки таймингов из 13e5bfe373
и относящуюся к индикации часть 04a9d989fb.
Сохранить текущую последовательность DATA/CLOCK ветки 167, удерживать
TESTLAMP при кратких провалах входа и исключить повторный вход Timer1.

Адаптировать тесты TESTLAMP к типу накопителя и кодировкам проекта 166.
Сохранить изменённые исходники и русские комментарии в UTF-8.

Проверка: поведенческие и мутационные тесты TESTLAMP, полная сборка
27 модулей TI C2000 21.12.0.STS и линковка, образ SCI8 30174 байта,
компиляция канала индикации с общей оптимизацией -O3.
2026-10-13 19:18:10 +03:00
41 changed files with 1728 additions and 2047 deletions

20
.gitignore vendored
View File

@@ -6,23 +6,3 @@
/UKSSTMS320F28335.paf2
/UKSSTMS320F28335.sbl
/Bin/UKSSTMS320F28335_*.bin
# Локальные разовые проверки, резервные копии и экспериментальные результаты сохраняются вне истории исходников.
/.ui_patch/
# Кэш pytest создаётся повторно при запуске тестов и не является частью прошивки.
/.pytest_cache/
# Загруженные исполняемые файлы и зависимости Doxygen остаются локальными инструментами.
/Doc/.tools/
# HTML, JavaScript и журналы генерации документации восстанавливаются из исходников.
/Doc/build/
# Каталог Release этого CCS-проекта содержит генерируемые make-файлы и объектные файлы.
/UKSSTMS320F28335/Release/
# Скомпилированный кэш Python восстанавливается автоматически и не содержит самостоятельных исходников.
__pycache__/
# Новые карты памяти формируются линкером; уже отслеживаемые карты остаются в истории.
/Bin/*.map
# Паспорт сопровождает генерируемый BIN и сохраняется рядом с ним в локальном выпуске.
/Bin/*.bin.manifest.json
# Контрольная сумма относится к конкретному генерируемому BIN и не является исходным файлом.
/Bin/*.bin.sha256
# Именованные экспериментальные Flash-варианты сохраняются локально; основные отслеживаемые OUT остаются в истории.
/Bin/UKSSTMS320F28335_flash_*.out

View File

@@ -1,4 +1,4 @@
:20000000AA0800000000000000000000000000000000000081B5670200002FB700FFC0DA10
:20000000AA080000000000000000000000000000000000006AB56202000017B700FFC0DA44
:2000200000000000C1C081C1400101C3C003800241C201C6C006800741C70005C1C581C4C4
:20004000400401CCC00C800D41CD000FC1CF81CE400E000AC1CA81CB400B01C9C0098008A5
:2000600041C801D8C018801941D9001BC1DB81DA401A001EC1DE81DF401F01DDC01D801CD4
@@ -19,932 +19,927 @@
:200240000000FFFF08D700000000FFFF0DD700006400FFFF0ED700007C92FFFF0FD70000A6
:200260000000FFFF11D700000000FFFF12D700000000EEFF2ED70000267E773A4EA4F43F45
:200280001B406ABF000000000000000000000000000000000000000000000000D8FF01DD25
:2002A000000048130D136C13F912A8078007320A0A0A0C138D130C13EF129E07AD07280A49
:2002C000370AEC0ECE0EB10D340DB606A10256030E00FB0EC90EC20D040DA1068D025C03ED
:2002E00011000105F8040000000001080208BD0ABE0AFFFFA2DE00000000FFFFA3DE00004C
:2002A00000000A1460133D13CE1359075907CA09D4099C13B0135613A6134F075707CB09F0
:2002C000D209320F190FEC13F212C206A30256030E00050FD80EB20C1C0C140400005C03AC
:2002E0001100090500050000000019081708D50AD30AFFFFA2DE00000000FFFFA3DE0000E1
:200300000000FFFFA4DE00000000FFFFA5DE00000000F4FFB2DE0000000000000000000059
:200320006AA200009CA2000065A2000060A20000FFFF30DE00000000FFFF31DE0000000051
:20034000FFFF32DE00000000FFFF33DE00000000FFFF34DE00000000FEFF3ADE000000005B
:200360000000FEFF3CDE000000000000FFFF18DF00000000FFFF19DF00000000FEFF26DF79
:2003200008A000003AA0000003A00000FE9F0000FFFF42DF00000000FFFF43DF00000000BC
:20034000FFFF44DF00000000FFFF45DF00000000FFFF46DF00000000FEFF4CDF000000000F
:200360000000FEFF4EDF000000000000FFFF18DF00000000FFFF19DF00000000FEFF26DF66
:20038000000000000000FEFF28DF000000000000FEFF2ADF000000000000FEFF2CDF00004B
:2003A00000000000EEFF2ADD00004A0C023E4A0C023E0000803F0000803F6C09F93B6C0981
:2003C000F93B6C09F93B6C09F93B0000803FFFFF08DE00000000FFFF09DE00000000FFFF11
:2003E0000ADE00000000FFFF0FDE00001400FFFF12DE00000100F0FFB0DA00003F00060069
:200400005B004F0066006D007D0007007F006F00400000000000000079000000FFFF77DFE0
:2004200000000600FFFF79DF00000000FFFF7ADF00000000FEFF7EDF000000000000FFFFB1
:2004400053DF00000000FFFF54DF00000000FFFF55DF00000100FEFF9CDF000025B70000B2
:20046000FEFF9EDF000025B70000FEFFA0DF000000000000FEFFA2DF000000000000FFFF2E
:200480003CDD00000000FFFF3DDD00000000FBFF8EDF000008000C00100000000400FFFF9E
:2004A00098DF00000A00FFFF99DF00000100FCFF94DF00000000000000000000FEFFA6DF54
:2004C000000000000000FFFF00D000000100FFFF40DE00000000FFFF41DC00000000FFFF18
:0A04E00029DD00000000000000000C
:2004EA002F370000008002FEA4C5A927013B008FC0DBC7924196A9A8AB81A98A01BEC418F2
:20050A0000E04192C4984148046CC44E026D00BE008FC0DBA9A8AB81A98A1F765C03A6CC4A
:20052A00010088FFC4CDFFFDA8CAA993D8FF01F1C49601910B9982FE060010E70B0001E824
:20054A00EAFA20E740000DE8BA9E6FE819F000E7100000E7490000E70000007710E74000FD
:20056A0040768AB602E8010250E80000407662B30600A71EA59243880960C4060561010222
:20058A004156C4000B6F00BE096FA706C40F036201BE046F01020156C400A6920600BDB264
:2005AA007AD178D0013BBDAA808DC0DB9A92929EA985A9BD120F007700770077007789E6F7
:2005CA00010002E84016407662B37BD11F765C0379D0013B03E226009A92929EA985A9BD45
:2005EA00120F007700770077007789E6010002E84016407662B300D11F765C0303E22800A7
:20060A00008FC0DB013BA9A8A181A98A74D0C8E29400008F40DFA9A8A181A98AC492025244
:20062A00046002E8D123056F02E8E1300AE80100407662B3013B008F2ADD0356A10101561D
:20064A00A40001D9A192045203E2C400DA64BE86BE8B0600BDB2BDAA03E2BD0404FE00523F
:20066A00A95906601F765C030B920B2B0C96A1921F765C0307540A63008FC0DB013BA9A8F9
:20068A00A181A98AC44FEDFF0102008F86DE013BA9A8A181A9837FD0008FC0DB944CC4E22C
:2006AA00C500EDFFEA01A19207544F63A9A8A181A98AC44D4A6C00BEA6921F767803A985B3
:2006CA0000D5A71E009B2E9207520260019B0356A8011F765C030793A99F2765013B008F95
:2006EA00C0DBA9A8A581A98AC492A9CD0080A9CC0060A8CA0A60008F86DEA9A8A581A98A11
:20070A0001DEA706C40DA71E1F767803009B2E9207520260019B0356A8011F765C0301DD17
:20072A000793A99FA555DC62A6920761A985421EA706C00037B6A71EA7BD120F00770077D5
:20074A000077007789E60000A192089E1F767803A988309242617FD0008FC0DB949316D02D
:20076A00D3FF9492C1FFA8CAA940146C1F7678032F9205520C6002E849280CE8010094E67E
:20078A00080014AD0465A6920250A988A692E3FF6801A1921F765C030754E2FF8401A64011
:2007AA000F6C79D0C8E29401007720E74000AFE2280400E72000007790E800760E6F78D080
:2007CA00C8E29401007720E74000AFE2260400E72000007790E800761F767803319269614E
:2007EA00A692E3FFDE001F7678032F9202521F61A1400F6C79D0008FC0DBC8E294011F76AF
:20080A005C0320E74000AFE2280400E720003B6F78D0008FC0DBC8E294011F765C0320E70E
:20082A004000AFE2260400E720002D6F08BEA192A672008FC0DB013BA9A8A681A9838076FF
:20084A0086DEA606A181A71E18D0C7929596A19206520D6378D0C8E294011F765C0320E7AE
:20086A004000AFE2220400E720000D6F78D0C8E294011F765C0320E74000AFE2240400E792
:20088A002000007716E8000D14AD136512E8401A14AD0F63A12D008F00D950E800B2123585
:2008AA000156A400407600B602E84116407662B316D0008FC0DB26E8000D94937FD0D0FF4B
:2008CA00949232E8401AC9FFA8CAA940036C02E8001150E80148A9A8013B18D08CE600007F
:2008EA00A1818CE60900A983A9A8A181A98A13E29501432BA6BF120FC493A892CDFF01F0B2
:20090A000190A980A61BECFF3461A692C852316130D09592FB9CA6540363A845186D008FCF
:20092A00C0DBA9A8A181A98A30D0A69294540F6248D0945428651F765C03A7CC01000B18C3
:20094A00F7FF82FF431A10000B981D6F4392A7CD01001F765C03205097FF0B18FBFFA91858
:20096A00FFFEA9CB43970356A70207900B980B6F1F765C03A7CC0100431A01000B18EFFF95
:20098A0083FF0B981F765C030E92E0FF8C00AD5CA19283DCA45C40760080EFFF8400808DD9
:2009AA00C0DB013BA9AAA681A98A8CE6000078D078D113E2940079D092929A9EA985A9BDE4
:2009CA00120F007700770077007789E604000077CFE6210002E80818407662B31F765C0387
:2009EA0003E2220002E8101CCFE621000FE83033407662B37AD11F765C0378D0013B008F83
:200A0A00C0DB03E224009A92949EA985A9BD120F007700770077007789E6010002E80818BF
:200A2A00407662B37BD11F765C0379D0013B008FC0DB03E226009A92949EA985A9BD120FD4
:200A4A00007700770077007789E6010002E80818407662B31F765C0303E22800236FA9A887
:200A6A00013BA681A98A8CE6000078D013E2940040765C80176FA9A8A181A98A8CE6000063
:200A8A0018D013E294000E6F008FC0DBA9A8A181A983A9A8A181A98AC52B18D0C51A0080B8
:200AAA00942B84FEAFE2BE04BE86BE8B0600BDB2BDAA02FE0052A92706601F765C030B9216
:200ACA000B2B0C96008FC0DB013BA9A8AB81A98AC492A94FEDFFCB00CDFF0190A92F422B77
:200AEA00AB92075260610356AB01A95A1F7678034218FFFB008DC0DEA22D3E06412B225607
:200B0A000190A95D0356A201A007A98A20FFE80340764680013B019B019089FFA2954298D6
:200B2A00A82D4218FFF73E06412B22560190A95D0356AC01A007A98A20FFE8034076468061
:200B4A0001908AFF4298AB9206522D60029A4218FFEFA294013BA92D3E06412B2256019090
:200B6A00A95D0356AC01A007A98A20FFE80340764680013B039B0190A2958BFF4298A82D24
:200B8A004218FFDF3E062256412B0190A95D0356AC01A007A98A20FFE8034076468001905D
:200BAA008CFF42984292C0FF42CDBFFF42CA008F86DE013BC0FF42CAC0FF42CAC3FF409003
:200BCA00A8CA4296A9A8AB81A9837FD0008FC0DBC4E2C500944C416D408F40DFA9A0AB8143
:200BEA00A9830356C501408F2ADD0156A500AFE2C50100E7400000778CE60000A9A8AB81EA
:200C0A00A6BF120FA98318D148D0A6929D7E95541163408F30DFA9A0AB81A983421A080084
:200C2A00A9A8C581A98AC443056CAA920360421A000142920761AA9205601F765C030B1A76
:200C4A0002001F765C030E921E60AD5CAB9282DCA45C40760080176FA9A8AB81A98A8CE6F4
:200C6A00000018D013E294000E6F008FC0DBA9A8AB81A983A9A8AB81A98AC52B18D0C51A42
:200C8A000080942B82FEBE86BE8B060000BE013B008FC0DBA9A8A681A98A01DE18D0A6922A
:200CAA00C41800E01852942BF46400BE013B008FC0DB408F80DFA9A8A681A98A0356A601F0
:200CCA000156A50070D0C8E294001F76780303E2C5002F9206521760408F40DFA9A0A681E8
:200CEA00A983C59203520F60A640056D18D094288601096F008FC0DBA9A8A681A98A18D0EB
:200D0A009428300201DEA6920452D2642F9201521060008FC0DB1F765C03A9A80781A98A8A
:200D2A00C41A0040CC1A0040D41A0040DC1A00401F7678032F9206520760008FC0DBC41A69
:200D4A000040CC1A00401F7678033492036140765C8006008076F9151F765C03408FF22B6D
:200D6A00008F3E4900B61202BDB2AC2B013B1AC21EA020A81F7678033EC31F765C031C1E66
:200D8A00008F40DFA9A8AC81A98AC42B008F30DFA9A8AC81A98AAC92019CC4211052A92DB4
:200DAA00F0641F7678032F9206521860008F40DF408F30DFBF56C4031F765C03BF56C501FD
:200DCA00BF56CC03CD2BBF56D402BF56D503BF56DC02BF56DD0210282C011F7678032F9238
:200DEA0001521F601F765C03AC2B013BBF5610C8008F40DFA9A8AC81A98ABF56C403008F59
:200E0A0030DFA9A8AC81A98AAC92AC9301F002910190A8CAC496AC92019CA92D0452E96456
:200E2A001F7678032E9209523460008F40DF408F30DFC42BBF56C50108D0BF56CC010ED1FA
:200E4A00CD2BBF56D404BF56D503BF56DC04BF56DD02BF56E404BF56E505BF56EC04BF56B7
:200E6A00ED04BF56F406BF56FC05BF56940609D0BF5694070AD0BF5694060BD0BF56940607
:200E8A000CD0BF5694060DD0BF569408BF569C08AC2B013B90E50356AC0180765EDF008F26
:200EAA0088DF5AFF0156A600A9A8AC81A98AC6C3408F6ADFA9A9C42B0156A500AC92019C01
:200ECA00A92D03E2C5000652E764AC2B013B00BE0356AC01008F42DC0156A400AC92019C8B
:200EEA00C4C2A92D1C52F564AC2B013B0356AC01008FC0DE0156A400AC92019CC4C2A92D4D
:200F0A002052F564AC2BC0762ED71235408F40D7008F00D90156A5000156A400AC92019C83
:200F2A0011F68524A92D185211F68424EF64AC2B1235008FC0DD0156A400AC92019CA92DBF
:200F4A00045211F68424F5641F7678032E92A993FF9D01532269A993FD9D015315690752A1
:200F6A000A61095222601F765C03BF56070FBF56080D1B6F1F765C03BF560608BF56070811
:200F8A00BF560808126F1F765C03BF560608BF560710BF560810096F1F765C03BF56060AA0
:200FAA00BF560714BF560814BE8B06001F76780333921F765C030780026104DF00D5013BD0
:200FCA0000D40356A5038076F0D8A4810156A60001DCA492C62B0852F56401DDA592025232
:200FEA00F0641F7678032F9204526D61A79200D52D65013BA592A2FF008FF0D8CBFFA59490
:20100A00A3FFA927A9A8AB81A61E0356AB04A92D019BA592ACEA189A67FFA594C699A988EB
:20102A00A2FFCBFFA594189CA3FFA92701DDA9A8AB81A98A0356AB04A92DA692ACEA019A07
:20104A0066FFC498A792A554D66200D5013B008FF0D803BEA9A8A581A71EA592A672A9A856
:20106A00A681A98A08D001DDC7929496A5920352EF641F7678032F92008FF8D8025204600C
:20108A00A9280003036FA928000FFC981F76780333920961408FF0D8A58A0FDCC41AFF00BF
:2010AA00F51A003F1F7678032F9206521260408FF0D8A58A0FDCC41A3000FD1A00E0CD1AA0
:2010CA000030076F008FF8D8C41A0F00F41AFC01008FF0D8A48308DC01DDC51A0100F41ADB
:2010EA000700FC1A00E00600BDB2BDAABDA203E2BD0403E2BD0503E2BD060CFE1F7678039F
:20110A0044962F9206520860449206601F765C030B920B2B0C961F765C030792008F30DF9A
:20112A00013B4494A988A9A84481A98A808DC0DBC4924696449201F0019047964492A0FF93
:20114A00A92D499603354896049AAC94A95BA9AAA681A98A457EC44FEDFFBE02008FBCDD81
:20116A00A9A84481A98A64BE4492A672C4E2C405A9AAA681A98A8CE62800408D80DF13E235
:20118A00C40044920356A9010156A1004C1E02E891340BE801E0AFE2C10020E7280040768C
:2011AA0062B3442D008FC0DD1235AFE2C10110E740000156A40003E2C100407600B6013B59
:2011CA008CE60000A9AA4481A6BF120FA98A70D0408F2ADD947E4C060156A500A685A9BDBB
:2011EA00120F007700770077007789E60000408D6ADF21E3C5414C0600E70C000156A1001C
:20120A0000E72101AFE2C10320E7C80002E8512B0BE80100407662B3AFE2C10110E74000E8
:20122A00007703E2C10040768AB6013B008F88DF8CE60000A9A84481A98A13E2C400449215
:20124A00A940ECFFAA001F765C03CFE62000AFE22A0140762A80452D008F00D912350156A9
:20126A00A400407600B64488008F40DF013BA9A8A681A98A1F765C0303E22C00C492035243
:20128A00086012E8000D14AD046390E503E22C00008F40DFA9A8A681A98AC49202520860BC
:2012AA0012E8B01414AD046390E503E22C0001E8A9FDAFE22C0508E89927407662B31F7657
:2012CA005C03CFE606000E921660008FC0DB4588013BA9A88CE6280018D0A681A98381DE1C
:2012EA00A9A8A681A98A13E295008CE6330018D013E29403013BCFE62000AFE22A01408DFC
:20130A006ADF0356A301AFE6000020E740000156A10000E70100AFE2C10020E7080002E876
:20132A00512B0BE80100407662B3AFE2C10110E74000007703E2C10040768AB6013B408FC0
:20134A0088DF8CE60000A9A0A381A61EABBF120F68B6C63F4892A772008FC0DBA9A8A78135
:20136A00A61EA9A04681A71EAA286900C792C696A9A04481A71E4892AA72A9A8AA81A61ED1
:20138A00AA286A00C792C6964892AA72A9A8AA81A98AC43F116F1F7678032F9201520660A0
:2013AA00CFE62000AFE60000036FCFE620001F765C0303E22A00008F88DFA9A84481A98337
:2013CA00A9A84681A98AC8E2C400008F88DFA9A8A381A98A4488C8E2C401008FC0DBA9A8F6
:2013EA00A681A98AC8E2C505432BC492CDFF01F00190008F88DFA959A9A8A681A98A96E6E4
:20140A00050096E60D00C8E2C400CFE6290020E72800407662B301E8C9F400D50CE8D1CCE2
:20142A0094E6080014AD0B651F765C03C8E21000007794E6050014AD026501D54492013B3B
:20144A00008F5EDF1F765C030356A901412B0156A4001E064076468000520A614393A1CCB8
:20146A000100045187FFA818FFFEA8CA4396008F88DF013BA9A8A381A98AC8E2C400CFE681
:20148A00290020E72800407662B301E8C9F400D50CE8D1CC94E6080014AD0B651F765C036C
:2014AA00C8E21000007794E6050014AD026501D5013B008F5EDF1F765C030356A301412B0F
:2014CA000156A4001E064076468000520A614393A1CC0100045187FFA818FFFEA8CA439683
:2014EA001F7678032F920652606047925E60008FC0DB013BA9A84581A98A30D0C8E2940074
:20150A00007794E6060014AD0A654393A1CC0100205187FFA818FFFEA8CA4396008F40DFA9
:20152A00A9A84481A98AC49203522760008FC0DBA9A84581A98A48D0412BC8E2940000D51B
:20154A0094E6060014AD026301D54992008F94DF1F765C030356A9010156A4001E0640765C
:20156A00468000520A614393A1CC0100085187FFA818FFFEA8CA4396008F40DF013BA9A878
:20158A004481A98AC49202521060008FC0DBA9A84581A98A48D0C8E29400007794E606006E
:2015AA0014AD0365431A10004492015204610352E0FF87004392E1FF84001F76780308925F
:2015CA007F601F76780328060C192A0F036726062A1E2A8A8CE62000A9A801092A1E13E2CA
:2015EA00C4002A8A1F765C03AFE22A00A9A81F7678038CE6000001092A1E13E2C4002A8A28
:20160A008CE62B00A9A801092A1E13E2C4032A83013B008F88DFA9A001092A1EA9A8448134
:20162A00A71EC792C5962A83A9A001092A1EA9A84681A71EC792C5962A83A9A001092A1E06
:20164A00A9A8A381A71EC792C5962A8A1F765C03A9A80109AFE22C001F7678038CE6000050
:20166A002A1E13E2C4002A8A8CE63200A9A801092A1E13E2C4022A83008FC0DBA5C4A9A818
:20168A004481A71E01DE70DF2AC2C792C5962AC5A70601092A1E1F765C0310921F76780354
:2016AA00C7962AC5A70601092A1E4392C7962AC5A70601092A1E1F765C030E92C7961F762F
:2016CA005C030E921560AD5C459283DCA45C407600800E6F008FC0DBA9A8A681A983A9A87B
:2016EA00A681A98AC52B18D0C51A0080942B8CFEAFE2BE06AFE2BE05AFE2BE04BE82BE8686
:20170A00BE8B06001B76F0FF00E2BD0030E60006422916562576006F1B76F0FF00E2BD0035
:20172A0030E6000602FE422916561F7633003892419623760008267600003892382B212890
:20174A00FFFF1029103B419238962576006F1B76F0FF00E2BD0030E6000602FE4229165640
:20176A001F763300389241962376000826760000381890002128FFFF1029103B419238966D
:20178A002576006F1B76F0FF00E2BD0030E6000602FE422916561F76330038924196237621
:2017AA00000826760000381890002128FFFF1029103B419238962576006F1B76F0FF00E2C3
:2017CA00BD0030E6000602FE422916561F76330038924196237600082676000038189C00BD
:2017EA002128FFFF1029103B419238962576006F1B76F0FF00E2BD0030E6000602FE4229C3
:20180A0016561F76330038924196237600082676000038189E002128FFFF1029103B419220
:20182A0038962576006F1B76F0FF00E2BD0030E6000602FE422916561F7633002292419661
:20184A0023760101221868002128FFFF1029103B419222962576006F1B76F0FF00E2BD00C2
:20186A0030E6000602FE422916561F76330022924196237601012292222B2128FFFF1029FC
:20188A00103B419222962576006F1B76F0FF00E2BD0030E6000602FE422916561F76330084
:2018AA00229241962376010026760101221878002128FFFF1029103B419222962576006F49
:2018CA001B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF00E2BD0030E674
:2018EA0000064229165610292576006F1B76F0FF00E2BD0030E600064229165610292576D8
:20190A00006F1B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF00E2BD00DA
:20192A0030E600064229165610292576006F1B76F0FF00E2BD0030E600064229165610291C
:20194A002576006F1B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF00E2BC
:20196A00BD0030E600064229165610292576006F1B76F0FF00E2BD0030E600064229165658
:20198A0010292576006F1B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF25
:2019AA0000E2BD0030E600064229165610292576006F1B76F0FF00E2BD0030E600064229A2
:2019CA00165610292576006F1B76F0FF00E2BD0030E6000602FE422916561F7633002292C0
:2019EA00419623760100267601012292222B2128FFFF1029103B419222962576006F1B76DC
:201A0A00F0FF00E2BD0030E6000602FE422916561F7633002C92419623762000267639014F
:201A2A002C922C2B2128FFFF1029103B41922C962576006F1B76F0FF00E2BD0030E60006E2
:201A4A0002FE422916561F7633002C92419623762000267639012C1832002128FFFF1029C3
:201A6A00103B41922C962576006F1B76F0FF00E2BD0030E6000602FE422916561F76330098
:201A8A002292419623760100267601012218F8002128FFFF1029103B419222962576006FE7
:201AAA001B76F0FF00E2BD0030E6000602FE422916561F7633002292419623760100267681
:201ACA0001012218FA002128FFFF1029103B419222962576006F1B76F0FF00E2BD0030E631
:201AEA00000602FE422916561F763300309241962376800026760000301803002128FFFF57
:201B0A001029103B419230962576006F1B76F0FF00E2BD0030E6000602FE422916561F76ED
:201B2A003300329241962376000126760001321875002128FFFF1029103B419232962576D6
:201B4A00006F1B76F0FF00E2BD0030E6000602FE422916561F7633003292419623760001FD
:201B6A00267600013218FD002128FFFF1029103B419232962576006F1B76F0FF00E2BD00E8
:201B8A0030E6000602FE422916561F7633003092419623768000267600002C182300212882
:201BAA00FFFF1029103B419230962576006F1B76F0FF00E2BD0030E6000602FE42291656E4
:201BCA001F763300329241962376000126760001321871002128FFFF1029103B4192329640
:201BEA002576006F1B76F0FF00E2BD0030E6000602FE422916561F763300329241962376C3
:201C0A000001267600013292322B2128FFFF1029103B419232962576006F1B76F0FF00E229
:201C2A00BD0030E60006422916562376390110292576006F1B76F0FF00E2BD0030E6000699
:201C4A00422916562576006F1B76F0FF00E2BD0030E600064229165610292576006F1B76B3
:201C6A00F0FF00E2BD0030E6000602FE422916561F7633002C9241962376200026763901ED
:201C8A002C1837002128FFFF1029103B41922C962576006F1B76F0FF00E2BD0030E600061A
:201CAA0002FE422916561F7633002C92419623762000267639012C922C2B2128FFFF1029C2
:201CCA00103B41922C962576006F1B76F0FF00E2BD0030E6000602FE422916561F76330036
:201CEA002C92419623762000267639012C1833002128FFFF1029103B41922C962576006FD5
:201D0A001B76F0FF00E2BD0030E6000602FE422916561F7633002C924196237620002676F5
:201D2A0039012C1822002128FFFF1029103B41922C962576006F1B76F0FF00E2BD0030E65A
:201D4A00000602FE422916561F76330038924196237600082676000038189F002128FFFFC0
:201D6A001029103B419238962576006F1B76F0FF00E2BD0030E6000602FE422916561F7683
:201D8A0033003892419623760008267600003892382B2128FFFF1029103B419238962576F4
:201DAA00006F1B76F0FF00E2BD0030E60006422916562576006F1B76F0FF00E2BD0030E659
:201DCA000006422916562376390110292576006F1B76F0FF00E2BD0030E6000642291656F4
:201DEA002376391110292576006F1B76F0FF00E2BD0030E600064229165610292576006F5E
:201E0A001B76F0FF00E2BD0030E6000602FE422916561F7633003092419623768000267690
:201E2A000000301801002128FFFF1029103B419230962576006F1B76F0FF00E2BD0030E6AC
:201E4A00000602FE422916561F7633003092419623768000267600002C922C2B2128FFFF29
:201E6A001029103B419230962576006F1B76F0FF00E2BD0030E6000602FE422916561F768A
:201E8A0033002A92419623761000267610012A922A2B2128FFFF1029103B41922A96257612
:201EAA00006F1B76F0FF00E2BD0030E6000602FE422916561F7633002A9241962376100093
:201ECA00267610012A1802002128FFFF1029103B41922A962576006F1B76F0FF00E2BD0080
:201EEA0030E6000602FE422916561F76330024924196237602002676000024181F002128B5
:201F0A00FFFF1029103B419224962576006F1B76F0FF00E2BD0030E6000602FE422916568C
:201F2A001F763300269241962376040026763D012692262B2128FFFF1029103B4192269666
:201F4A002576006F1B76F0FF00E2BD0030E6000602FE422916561F7633002492419623766D
:201F6A0002002676000024180F002128FFFF1029103B419224962576006F1B76F0FF00E2AA
:201F8A00BD0030E6000602FE422916561F763300269241962376040026763D012618260055
:201FAA002128FFFF1029103B419226962576006F1B76F0FF00E2BD0030E6000602FE42290D
:201FCA0016561F7633002492419623760200267600002492242B2128FFFF1029103B41925C
:201FEA0024962576006F1B76F0FF00E2BD0030E6000602FE422916561F76330026924196AA
:20200A002376040026763D01261826002128FFFF1029103B419226962576006F1B76F0FFF7
:20202A0000E2BD0030E6000602FE422916561F76330024924196237602002676000024183C
:20204A0008002128FFFF1029103B419224962576006F1B76F0FF00E2BD0030E6000602FED1
:20206A00422916561F763300269241962376040026763D012692262B2128FFFF1029103BDD
:20208A00419226962576006F1B76F0FF00E2BD0030E6000602FE422916561F76330024920D
:2020AA004196237602002676000024180C002128FFFF1029103B419224962576006F1B76CD
:2020CA00F0FF00E2BD0030E6000602FE422916561F763300269241962376040026763D01A7
:2020EA002692262B2128FFFF1029103B419226962576006F1B76F0FF00E2BD0030E600062E
:20210A0002FE422916561F76330024924196237602002676000024180E002128FFFF102988
:20212A00103B419224962576006F1B76F0FF00E2BD0030E6000602FE422916561F763300D9
:20214A00269241962376040026763D0126183E002128FFFF1029103B419226962576006F8F
:20216A001B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF00E2BD0030E6CB
:20218A00000602FE422916561F76330028924196237608002676080128180F002128FFFF23
:2021AA001029103B419228962576006F1B76F0FF00E2BD0030E6000602FE422916561F764F
:2021CA00330028924196237608002676080128180F002128FFFF1029103B419228962576A5
:2021EA00006F1B76F0FF00E2BD0030E6000602FE422916561F76330028924196237608005A
:20220A0026760801281803002128FFFF1029103B419228962576006F1B76F0FF00E2BD0047
:20222A0030E6000602FE422916561F76330028924196237608002676080128180300212876
:20224A00FFFF1029103B419228962576006F1B76F0FF00E2BD0030E6000602FE4229165645
:20226A001F7633002A92419623760800267608012892282B2128FFFF1029103B419228964A
:20228A002576006F1B76F0FF00E2BD0030E6000602FE422916561F76330028924196237626
:2022AA00080026760801281802002128FFFF1029103B419228962576006F1B76F0FF00E25D
:2022CA00BD0030E6000602FE422916561F76330032924196237600012676000132187500EB
:2022EA002128FFFF1029103B419232962576006F1B76F0FF00E2BD0030E6000602FE4229BE
:20230A0016561F7633003292419623760001267600013292322B2128FFFF1029103B4192EE
:20232A0032962576006F1B76F0FF00E2BD0030E6000602FE422916561F763300329241964C
:20234A002376000126760001321831002128FFFF1029103B419232962576006F1B76F0FFD1
:20236A0000E2BD0030E6000602FE422916561F763300329241962376000126760001329263
:20238A00322B2128FFFF1029103B419232962576006F1B76F0FF00E2BD0030E6000602FE2B
:2023AA00422916561F7633002E92419623764000267600002E922E2B2128FFFF1029103B84
:2023CA0041922E962576006F1B76F0FF00E2BD0030E6000602FE422916561F7633002E92B8
:2023EA00419623764000267600002E1828002128FFFF1029103B41922E962576006F1B761C
:20240A00F0FF00E2BD0030E6000602FE422916561F7633002E92419623764000267600005D
:20242A002E1820002128FFFF1029103B41922E962576006F1B76F0FF00E2BD0030E6000685
:20244A0002FE422916561F7633002E92419623764000267600002E1838002128FFFF1029C9
:20246A00103B41922E962576006F1B76F0FF00E2BD0030E6000602FE422916561F7633008C
:20248A002E92419623764000267600002E1838002128FFFF1029103B41922E962576006F3C
:2024AA001B76F0FF00E2BD0030E6000602FE422916561F7633002E9241962376400026762C
:2024CA0000002E183E002128FFFF1029103B41922E962576006F1B76F0FF00E2BD0030E6CD
:2024EA000006422916562376390110292576006F1B76F0FF00E2BD0030E6000602FE422939
:20250A0016561F763300229241962376010026760101221848002128FFFF1029103B41929A
:20252A0022962576006F06000052A888C48A096509520763FF9CC4CDF8FF0790A8CAC49640
:20254A00A69245520D614E5208614F520D60C41A2000C418BFFF086FC418DFFF056FC41A02
:20256A002000C41A4000A59201520360C4187FFF02520360C41A8000C418EFFFC418F7FF1B
:20258A000600BDB200D5013B008F40DEA9A8A581A98AC47D01DDA5924652F76433D1008F78
:2025AA0040DE34D0BF569C0CBF56940C1F76400335D03BD1BF56940836D0BF56940937D024
:2025CA00BF56941038D0BF56940839D0BF56940C3AD0BF56940C3DD0BF569C053CD10092A0
:2025EA00FE9C949641D00092FF9C94963ED0BF56940810D0BF56DC08BF56F408BF56940D46
:20260A00BF56EC1BBF569C12BE8B0600A9BD120F00770077BDB21F7671038BE60100A48BF4
:20262A0050E8094088E23C00407662B388E6000000770077A9BF120FC18AA993B7FFFF91EC
:20264A00D497C18AFF90DC96BE8B06000852A927A4C50960089A01D54E9B40762190008D19
:20266A003B03972BAB9209520B60089AA78A01D54E9B40762190008D3B03BF5697010600D6
:20268A00A71EBDB2A492BDAA0152BDA22A60008F5070C08D40D01F76410300A822761F76C9
:2026AA00BE01408F11B209CDFFF7A81A0004008FCCB1099709CDFFFD1F763700A81A000120
:2026CA0000A01F76BE0109971F76370002A81F763300321A0100321A0200237600011A7659
:2026EA0002523360008F5077C08DC0D31F764F0300A82276408F806F1F76BE01FDCDFFEFC2
:20270A00807687B1A58AA81A0020079716DD07CDFF3F1ADCA81A008007971F763700C5184E
:20272A00CFFF408F42B1C41A040004C21F763300321A04001F76370006A01F763300321AB9
:20274A000800237600011A76D396A38A01D54E9B69FF089AC38640762190A2C4A2830BD0C8
:20276A00A28A0CDE0CDDC61A00400ADCC518FFDFCA18BFFFCA18DFFFCA18F7FFCA18FBFF7B
:20278A00CA1A0200CA1A0100C418FFBF92CCE0FF0BD00150929640764E90A38AA706407615
:2027AA009390D392025205601F76BF01081A0400C38A099ABF56E402A38A4076B390408D75
:2027CA0033030CD0932BA28A008D9E01932B9B2B0BDCC41A4000CA1A2000BE82BE86BE8B6D
:2027EA0006001F764103BF5602011F764F03BF5602021F764003009606000002008D420390
:20280A00941E0600BDB2BDAAA486008D3A0392923660A9AA13FF6700A98A008D36030102AA
:20282A000156C4009206008D3803920F0466C28ABF56E40208D092C5A7C408D0090201DE65
:20284A0092C2C207A98A008D3603C792C4969206008D3803920F4966C28AE446FE6CA28AE9
:20286A00099A4076B390D292025205601F76BF01081A0400008D9E01922B376F008D3803C3
:20288A00928A408D3803A492008D36030190A98801DC9AA89206008D3803920F0466C28A76
:2028AA00BF56E402A6920B6008D092830902C207A98AC592A7FFFF90C4960D6F08D092832D
:2028CA00A5C408D0090201DE92C2C207A98AC592FF90C496008D36039206008D3803920F6C
:2028EA000466C28AE446FE6C1F763300211A0001BE86BE8B0600BDB200BEACA0BDAAA486E3
:20290A00BDA2A98201DEA61B3075FD69D292025205601F76BF010818FBFF00BE01DEA61B8E
:20292A001027FD69408D3603A9AB08D09AA20109921E008D3A03922BC28AE446FE6CA28A35
:20294A00089A4076B39001BE008D38030102A30F92C21C68A7AB0902C207A98AC792C496B2
:20296A00C28AE446FE6C00BE01DEA61BE803FD69A28A099A4076B390D292025210601F7639
:20298A00BF01009A081A04000B6FC28A0902A7ABBF56E401C207A98AC792C496009ABE8207
:2029AA00BE86BE8B0600BDB2BDAAA486EFFF2A01C28AFCCCFF00A958EC47EDFF1D01408D43
:2029CA00A0019A92E0FF1E01408D3B039A920152116000BE408D3D03BF569A01408D3403A8
:2029EA009AC2408D3F039A2B408D3E039A2B0DD19A2B408D3F039A92E0FF0401408D3D03FB
:202A0A009A924F60F2C4208F0000A9A8A60F06662F8F0000A9A8A60F0867608F00000AD1FD
:202A2A00208F0000F2A09AA80CD19A923A52E0FFE900408DA0019A922E60408D34039AC422
:202A4A00A692A94001DE9AC2066DF28A0356A008C496076FF28AA9A80109F21EA092C498DB
:202A6A00008D34039206E20FE8FFCC000FD10CD0BF569A33A28A099ABF5692334076B3900C
:202A8A00408D3F03008DA001BF569A01BF569201EFFFB800008D3F03BF569201EFFFB200DA
:202AAA00408D3E039A923E60A0921F7604031D5425611F767E031954036019921F60A08837
:202ACA00408D31039A0EA60F0560408D33039A921560A0921F767E0318540760189205615A
:202AEA00408D33039A920A61408D3F03008D3D03BF569A01922BEFFF8500408D32039A7862
:202B0A00408D3E039A80019A013BA7949A96A9AAA781A98A0ED1089A9C78A28A4076B390D9
:202B2A00706F9A80019A013BA7949A96A9AAA781A98A0ED19C78408D3E039A9207520E60AE
:202B4A000DD19A9210520A60408D33039A9206600A9A1F767903A0941096408D3E039A9237
:202B6A0002521A60A0920DD103529A78336842523166008F40DE949203522C68A0923352CD
:202B8A000B600CD19A923A522560A09233520460008D3D03922B0DD09258408D3E03008F9D
:202BAA0040DE9A9294540467A91B90012A68A28A099A4076B390408D3D039A2B0DD0408D43
:202BCA003F03BF569A01408DA001BF569A01922B186FA28A099A4076B390408D3E039A2BFC
:202BEA000DD0408D3D03BF569A01408D3F039A2B922B076FC28ACC18DFFFC28ACC1A2000CA
:202C0A00C28AEC46EDFFD6FE1F7633000BDC211A0001C41A4000BE86BE8B0600BDB2BDAAFA
:202C2A00A486A61EBDA2A582D292025205601F76BF010818FBFF408D360308D09AC292A21C
:202C4A00008D3A03BF569201C28AE446FE6CA28A089A4076B390008D38030102921EC28391
:202C6A0000BE0902BF56E501C207A98AC392A7FFFF90C496A692BE82BE86BE8B0600227603
:202C8A001F76780316D0008F806F359294CDFCFF03901F76BE0118D0A8CA16961F76780396
:202CAA00369294CDFCFF03901F76BE011AD0A8CA18961F7678033792019094CDFEFF1F7698
:202CCA00BE01A8CA1BD01A961F767803389201901F76BE0194CDFEFFA8CA1B961A7669FFE6
:202CEA00060016D0008F806F1F7678039492039018D03596949203901AD0369694920190BE
:202D0A001BD0379694920190389622761F76BE011618FCFF1818FCFF1A1A01001B1A0100C1
:202D2A001A7669FF06001F7678032F9302531660008F880A1F76BF01A94002A8036C041A58
:202D4A000008A941036C041A0002A942036C041A8000A943036C041A080003533B60AB28AB
:202D6A00CB0BAA2800091F76BF01248F0000005202A90AA80360041A000801520360041A84
:202D8A00000202520360041A800003520360041A080004520360041A020005520360041A43
:202DAA00000106520360041A400007520360041A010008520360051A000809520360051A53
:202DCA0000010A5203600D1A20000B5203600D1A040001531E60AB2BAA28000B1F76BF011D
:202DEA00128F0000A94004A90CA8036C031A0008A941036C031A0001A942036C031A00025A
:202E0A00A943036C0B1A0200A944036C0B1A1000060022761F76BE01408F806F06180000C7
:202E2A000F8F00F0A5C4071800FF08183F00091800FF1BDD0BDE1606A918C0FF161EA9A8F2
:202E4A0018C0A89219C0C618EFFF0B18DFFF0B18BFFFC518F7FF1A76008FC06F1F7678039E
:202E6A00BF562E0109D0CC92C4FF01F001902E721F76BF010192C3FF1F76780301F00190AC
:202E8A0080FF2E721F76BF010192C5FF1F76780301F0019081FF2E729492C2FF01F0019042
:202EAA0082FF2E942E960352036104520360BF562F010152036102520360BF562F0305523E
:202ECA00036106520360BF562F0607520360BF562F0208520360BF562F04095209612F92F2
:202EEA00015208610552066106520461056FBF562F05BF5634012F92015203610652036057
:202F0A00BF5633010252036103520360BF5631010152036105520360BF5630010152036139
:202F2A0003520360BF56320202520360BF56320103520F6101520D61065206611F76BF01ED
:202F4A000D1A00100A6F1F76BF01021A8000056F1F76BF010B1A001006520E610352096142
:202F6A00015204610D1A0080096F0B1A0080066F0B1A0040036F021A800006520B610452C9
:202F8A000961025204610B1A0004066F0B1A0040036F021A000206520661045204610B1AD2
:202FAA000020036F021A0800227604520A6204522461015240610252346103522661446FB0
:202FCA000552116106524060088F8A021F76BE01059AA82801FC0AA81A1E1F76BF01021AE8
:202FEA000800326F088F00001F76BE01049AA82801BC0AA81A1E286F088F49031F76BE0150
:20300A0005020AA81A1E206FA928CB0BA82808091F76BE010A1E059AA828257C1A1E146F57
:20302A00088F8A0B1F76BE01059AA82801F80AA81A1E0A6F2FFF10161F76BE010A1E059ACC
:20304A00A82813BC1A1E1A7669FF0600BDB2BDAABDA202FE1F7678032F9200D200D3025292
:20306A0032601F76BF0116D0008FC0DB0992C1FF94CDFEFF01F016D116D00190A8CAA9CD5F
:20308A00FDFF949616D0099203F01F766F030290A8CA9C9600CDFFF39492039089FFA8CADD
:2030AA0000961F76BF010040036C0102A9860042026C02DA004A026C04DA0044026C08DA84
:2030CA0010D0947A1F7678032F9201523160408DC0DBA18A1F76BF01A183877620A116DC87
:2030EA00C418FDFF16DD0992C3FFA18A03F00290C59816DCC418FBFF0992412B008F3ADE1B
:20310A00C1FF01F00190A95DA606407646800356A902A18A1F766F0316D0079016DCC49834
:20312A0000CDFFE79192069089FFA8CA00961F7678032F9204525E6016D0408DC0DB1F76C1
:20314A00BF019192008F3ADE0190419601CC0004C9FFA95D20FFE8034076468016D00190D7
:20316A001F76BF0191CDFEFF16D0A8CA9196014B026C01DA0148026C02DA0945026C04DA54
:20318A000942026C08DA014A026C10DA0147026C20DA0944026C40DA0044046CA9AA8009D2
:2031AA00A9860149056CA9AA18FF0100A9860941056CA9AA19FF0100A986004B056CA9AA1C
:2031CA001AFF0100A9860045056CA9AA1BFF0100A9860940056CA9AA1CFF0100A986004AA7
:2031EA00056CA9AA1DFF0100A98610D0008FC0DB947A1F7678032F92065210601F76BF01AF
:20320A000046026D01DA0048026D02DA004B026D04DA10D0008FC0DB947A1F7678032F9200
:20322A000552E0FF89001F76BF010945036D01DA026F10DB0944036D02DA026F40DB004214
:20324A00036D04DA056FA9A21CFF0100A9820044036D08DA056FA9A21DFF0100A98200462D
:20326A00036D10DA056FA9A21EFF0100A9820040036D20DA056FA9A21FFF0100A9820048E7
:20328A00036D40DA056FA9A21FFF0200A982004A056DA9AA8009A986056FA9A21FFF0400E8
:2032AA00A9820043066DA9AA18FF0100A986056FA9A21FFF0800A9820045066DA9AA19FF56
:2032CA000100A986056FA9A21FFF1000A9820047066DA9AA1AFF0100A986056FA9A21FFF69
:2032EA002000A9820041066DA9AA1BFF0100A986056FA9A21FFF4000A982004B066DA9AACF
:20330A001CFF0100A986056FA9A21FFF8000A9820049066DA9AA1DFF0100A986056FA9A2B6
:20332A001FFF0001A9821F76780310D0008FC0DB3EA2947A008FC06FCC92013B1F76BF0184
:20334A00C4FF01F0019009D0A985A9860192C3FF01F0019080FFA9850156A2000192C5FF14
:20336A0001F0019081FFA9850156A2009492C2FF01F0019082FFA9850156A20017D0008F93
:20338A00C0DB82FE947ABE82BE86BE8B06000C52A9580567008F00C19492026F009A0600D5
:2033AA00BDB200BE00D0408FC0DBAA2B008F80DCA9A8A00DA98A9580C427A70EAB0F0361D8
:2033CA00C47F01BE01D8A0928052F168A6921061408D80DC809AA18A407625B480D0A828E5
:2033EA0002019196A18AA9287E3F4076FDA3BE8B0600A888A927BDB2A9A8A48B1F767E0336
:20340A00BDAA10FFA101408FFFFFA71EA986A98AAB9219278050C73FCF960302D77E4076CE
:20342A0014B4A9CDFF00A7FFA283008D9E01FF90A18ADD97E5960502BF56910140760892A7
:20344A00BE86BE8B0600BDB2BDAAA486008DA10100D5A9AAA50DA71EA9AAA50DA98A01DDEB
:20346A000EDFC7929496A5920852F468A28A0EDC0356D408DCCAA9590356E408ECCAA993BB
:20348A00A11B00120468A11B0C123168A11B00101067A19280522B677F520561008FC0DB3A
:2034AA009C97126FA8924076B5A540766A9E0C6FA1924076CC9D00520760069AA28A039B56
:2034CA0040768697196F008D9E01A11B0010BF569201036808BE026F0ABEA9AAA28A10FFEF
:2034EA00A101A983A60640760892066F069A029BA28A40768697BE86BE8B0600A98800B6CD
:20350A00A61B00120C68A61B0C12096700530761A9280C12A69EA854026801B6A7920600C1
:20352A00BDB2BDAABDA204FEA4860EDC0356D408DCCA42960356E408ECCA4396412BFF9CA8
:20354A007D52E7FFCC00429280520567809A429E435429674292A91B00101E68A91B0F109C
:20356A000666A9281010429E43541D674292A91B10101268A91B88100667A9288810429EA5
:20358A00435411674292A98820FF0011A60F046043927C5208614292439340760B98005263
:2035AA00E1FF9D00A9AA10FFA101A71E1F767E031992C79643920356A901BF56CF03D79671
:2035CA004292A91B00100868A91B001205670876303BBE924196439200D332614292A91BA4
:2035EA0000121067A91B00100767A358A072008FC0DB94880E6FA39242944076A69EA988F0
:20360A00086FA3924294A90800EE40765497A9880356A301A98003D1A9AA10FFA101A98A7C
:20362A00A793A17304D0A693D7FF9C97A793A073A6CCFF0001DB94964392A354D066429282
:20364A00A91B00100768A91B001204674192BD96007643920356A9014196A993408FFFFF88
:20366A00039AA894A959A9AA10FFA101A982A98AA10E407614B4A9CDFF0004D09B97419387
:20368A00A073A7FFFF9005D193964192A172069A4194A988079A9B2BA6594194A993419203
:2036AA009B2BA85908D0A0729B2B932B008D9E01BF569201421B0010036805BE026F08BE25
:2036CA00A9AA10FFA101A28AA98343920356A901A694A90E40760892066F039A029BA28A90
:2036EA004076869784FEBE82BE86BE8B0600BDB210FEAD5C89DCA45DAD5CC0760CC18FDC35
:20370A00A45C08F6852400D0C07615C105F68424008FC0DB1F7678032F9206520460A09290
:20372A007F520261942B01D8A0928052F4684076EBA4008FC0DBA4C51F7678032E921F7616
:20374A0078037EDFC7962F920452086160D162D0BF569C0ABF569408116F1F76700300D083
:20376A00BF56200AAD5C8FDCA45C9492008F22DC949601D8A0920652F6681F767803349218
:20378A001F767003BF5621321A611F7678032E882E92095283DE026004BE00D00129008F45
:2037AA0034DC0356A603C07601DDA00D0156A700C792949601D8A0920852F4681F767803DA
:2037CA002F920152116000D0008FC0DBA9A8A00DA98A01D830D1A092BF569C46105248D1B1
:2037EA00BF569C3CF2681F7678032F920252216000D0008FC0DBA9A8A00DA98A01D830D1CD
:20380A00A092BF569CAA065248D1BF569C96F26806D0008FC0DBA9A8A00DA98A01D830D1F4
:20382A00A092BF569C73085248D1BF569C69F2681F7678032F920352116000D0008FC0DBB0
:20384A00A9A8A00DA98A01D830D1A092BF569C46145248D1BF569C3CF2681F7678032E9294
:20386A0009521B6000D0013BAD5C89DCA480008F40DFA7069481A71E008F08DCC79294969F
:20388A0001D8A0920F52F1683DD1008FC0DB3ED0BF569C46BF5694461F7678032F92065204
:2038AA002560008FC0DB30D032D19428A401A4C531D09428A40133D09C2884037ADF9428BD
:2038CA00840348D094282C0149D094282C014AD0942820034BD094282003C792A4C56ED160
:2038EA006FD09C967BDFC79294961F76780333920A6100D0008F30DC9428000801D8A092F6
:20390A000452FB684076D38390FEBE8B0600BDB2408D80DCA9287E3FA8280201A18A407621
:20392A0040A4809AA18A407625B4A98080D0A988910EA70F0E60A688012920FFFFFFA60F2E
:20394A0008611F7678032E887ED0910EA60F1561407604994076EBA440765D971F767803C4
:20396A002F9201521F601F7670033F1A10001F765C030F28FFFF166F00D01F7678032F92EA
:20398A0006520460A0927F520761008F80DC9492008FC0DB949601D8A0928052EF684076A7
:2039AA00EBA4BE8B06000600A983C57C0600BDB2BDAABDA206FE1F767803A482BF56080119
:2039CA000EDCEC92FF90A95AE492FF90A988A9AA37FFA6AFA986DC92FF90A988A9AA37FFE9
:2039EA00A6AFA986D492FF90A98809D0A9AA37FFA6AFA9869492FF9008D0A9599492FF90E9
:203A0A00A988A9B237FFA6AFA98BFC92FF90A988008D3203A9B237FFA6AFA98BF492FF9012
:203A2A00A988408FFFFFA9B237FFA6AFA98BA9A210FFA101A71E441EA98A9392C796020288
:203A4A00BF56CC34407614B4A90E461EA28A465DA9B24076EAB3419644830102A38A4076AD
:203A6A00089244830102A38ABF56C534407608924192A90E461EC38AE446FE6CA9B2A38AF6
:203A8A00A28340769B93C38AE446FE6CA9A210FFA101A983A38A4692A993FF91A7FFFF9004
:203AAA00C597CD960602D52BDD2B4076089286FEBE82BE86BE8B060006000600A98AC492F1
:203ACA000600A988BDB21F767103A9A8008D3203A48BBF560101BDAA10FFA101408FFFFFEF
:203AEA00A71EA986A98A9193C7970202CF7E407614B4A9CDFF00A283A7FFFF90A18AD597DE
:203B0A00DD960602E52BED2B40760892BE86BE8B0600BDB202FEA48B0EDCEC92FF90A98057
:203B2A00E492FF90A988A70637FFA6AFA71EDC92FF90A9880129A70637FFA6AFA71ED4928D
:203B4A00FF90A988412BA70637FFA6AFA71EFC92FF90A9934196F492A838419620FF008026
:203B6A00A70F0666A706415C4076619A086FAD5CA792029B81DCA45C4076FDA3399AA18A17
:203B8A004076EE9A82FEBE8B0600BDB2BDAA02FE1F767103A486BF5601010EDCEC92FF90F7
:203BAA00A980E492FF90A988A70637FFA6AFA71EDC92FF90A988A70637FFA6AFA71ED4920E
:203BCA00FF90A988A70637FFA6AFA71E012920FF0080A70F0666A7064076EB9A4196086FFD
:203BEA00AD5CA792029B81DCA45C407640A4A9AA008D320310FFA1011F767C03A71EA98B12
:203C0A00A98A9292408FFFFFC796BF56CF384192FF90D7964192A7FFFF90DF961292A9CD97
:203C2A00FF00A7FFFF90E197E9960602407614B4A9CDFF0009D0A7FFA183A28AFF9008D11D
:203C4A00F597FD960A029D2B952B4076089282FEBE86BE8B060002FEA61EAC281F00013B51
:203C6A002256A60740FF0156A400C492A640036CFF90036FA7FFFF9082FE0600BDB2A48BD6
:203C8A00BDAAE1C50AD09183A706A58A02194076B89BA927A706A58A01194076B89BA1C5EA
:203CAA00008D3203408FFFFF0EDF0356A908AB94A95AA78A9192C7960202BF56CF334076B0
:203CCA0014B40AD0A95DE106918A02194076EAB3A988A20EA60F1061008D3C03912BD192D0
:203CEA00025205601F76BF01081A0400A18A099A4076B390096FA18A339A4076EE9A008D84
:203D0A003C03BF569101BE86BE8B0600A4830EDDED92FF90A980E592FF90A988A70637FFBD
:203D2A00A6AFA71EDD92FF90A988A70637FFA6AFA71ED592FF90A98809D0A70637FFA6AFFB
:203D4A00A71E9592FF9008D0A90EE41E9592FF90A988E40637FFA6AFE41EFD92FF90A988A0
:203D6A00E40637FFA6AFE41EF592FF90A988E40637FFA6AFE41E02020156E4000AD01F7656
:203D8A0078033A9AF4C394C3BF5608014076EE9A06000600BDB2BDAABDA2A482008DA001CB
:203DAA0093923D61408DA0010FD09B2B935A008F40DE013BA9A8A281A98AC4920352326861
:203DCA00A91B90012F66A29233521A61008F40DEA9A8A281A98AC458A059408FFFFFA38A58
:203DEA0082D981D8A1880EDC035694089CCAA959A606407614B4A188A90EA60F05600CD095
:203E0A00A292937A1C6FD392025205601F76BF01081A0400A38A099A4076B390A928FFFF9B
:203E2A000E6FD392025205601F76BF01081A0400A38A099A4076B390A928FFFFBE82BE8646
:203E4A00BE8B0600BDB206FEA48B0EDCEC92FF90A90E421EE492FF90A988420637FFA6AF50
:203E6A00421EDC92FF90A988420637FFA6AF421ED492FF90A98809D0420637FFA6AF421E45
:203E8A009492FF9008D0A90E441E9492FF90A988440637FFA6AF441EFC92FF90A9884406BD
:203EAA0037FFA6AF441EF492FF90A9880DD0440637FFA6AF441E9492FF900CD0A90E461E05
:203ECA009492FF90A9880BD0460637FFA6AF461E9492FF90A9880AD0460637FFA6AF461EE1
:203EEA009492FF90A9880ED0460637FFA6AF461E94CCFF0004520A61055212604206448ABA
:203F0A0046C4A693407640A4076F4206448A46C4A6934076FDA33C9AA18A4076EE9A86FED2
:203F2A00BE8B06001F76BF0100920093C5FFD7FF01F001F10190019190FFA9CB0092CAFFB0
:203F4A0001F0019081FFA8CA060009D0008FC06F1F76BF0194CD0004D9FF09CC004090FF10
:203F6A00CDFFA9CBC492C2FF01F0019081FFA8CA06001F7678030B93BDB2A98801537E60E6
:203F8A001F7678032F93065379601F765C030D9375601F767803099308CB6E607FD0008F7F
:203FAA00C0DB179394271660AB46036C1946086CAB47036D1947046D00530C600E6F0C1BB8
:203FCA00900103680C288F01182B1728E803A61A00010C1B1F034D67A6920552056018934D
:203FEA000361A61A0002AC28200311300E0DA98A32FFA71E608F0000A9A831FFA7070156A0
:20400A00A5002206C5962206CD97ABCDC000D57EDD9740761F9DE5964076329D08D1C07662
:20402A00C0DB1CDF09D0ED961F767803C792C076A6DFF596BF56FD06BF569D100AD1C792C7
:20404A0095960BD0CF929D96952B193FE5921A96A9282003ED930E0A1B970E5402600E2B42
:20406A000C1B200302670C0A010201562400066FBF561801036FBF560B03BE8B0600BDAA01
:20408A00AA280100A888A9800876303BBE5AA75D008F0010A9A8A50F2B60A6935461A75DC4
:2040AA00A9A8A50F2560A693015322601F7678032F9206521D601F765C030D9219601F7616
:2040CA0078030B92015214610C2B7FD00E2B172B162B008FC0DB0D2B102B94921996092B0E
:2040EA00082B019ABF560B014076469D316FA75D20FF0110A50F0B601F7678030B9201523B
:20410A000661A6930461A61BE8031B69A75D20FF0210A50F0460A61B20030F68A78020FF72
:20412A000310A70F0860A692015205661F767803167E0E6FAA2B0C6F1F7678030D7E086FCB
:20414A000F7E066F1F767803BF560B03172BBD7A0076AA92BE8606001F7678030C93118863
:20416A000E92150A111C0100172B0C2B1397127E14960E2B06000876303BBE2F1F767803C6
:20418A000B9201521860010201562200100A0F9210540566102B019A4076469D1F76780328
:2041AA0017920961FF9C17960660059A4076469D4076399EBD57007606000876303BBE2F09
:2041CA00049A4076469DBD57007606000052A88005611F7678031393046F1F7678030C9356
:2041EA00005203611488026F0E88005203611227026F1127A7550366009A1B6FA692A89EBD
:20420A00A794A9082003A91B20030668A908E0FCA91B2003FC67AC288025AB32AC280C0023
:20422A00A930AB07A40D208F00000156A400C4920600A988A693A80800F00F533B660356CC
:20424A00A601C07646C1A927AB0800E0A706AB0DA71EC7C52076A9284D55EFFFA000049A22
:20426A00EFFF9D000C9AEFFF9A00A9282003EFFF96001F7678030F92EFFF91001F767803C3
:20428A002206EFFF8C001F7678032206A80EEFFF86001F7678032F92EFFF81001F767803C0
:2042AA002E927C6FA928E803796F1F7678031792756FA61B10105D68A61B88101D671F76F0
:2042CA007803169304600B9301531661A692A908F0EFA988AC28ABAAA636AC281300225686
:2042EA000D93A92D0C31A692AC95ABEAAB5C16924076739E536FA61B00113B68A61B7C11F8
:20430A0038671F7678030C930A530468AB280A00026F0C27A60800EF2406AB01A71EA6928B
:20432A000360A7923B6F01520460A706A80E366F02520360AB92326F035205601F7678030F
:20434A0015922C6F84DEAC28ABAAA636AC2813002256A92DAB92AC540C690C93A692AB9FA2
:20436A000C31ABEAAC95AB5C009A4076739E166F009A146F1F767803169205611F767803E8
:20438A0013920C6F0C920A6F1F76780316920361039A046F1F7678030B9206001F767803ED
:2043AA000B92015227601F7678032F92065222600876303BBE2F7FD0008FC0DB94881F76D1
:2043CA007803190EA60F116040761F9DA9881F7678031A0EA60F09604076329DA9881F76C7
:2043EA0078031B0EA60F0461039A4076469DBD570076060004FEA9271F7678032F92065234
:20440A0033600876303BBE2F4397C076008000D0ABCDFF1FA888427C417DA793ABCF17615B
:20442A00A65D20FF3101A50F0860AD5C83DCA45C1F767E0394922696A69308530860AD5C9D
:20444A0083DCA45C1F767E0394922796A79301D8D0FF01DEA880A0930353E068029A4076EE
:20446A00469DBD57007684FE0600608F00001F767803208F00FA26A028A826062A1E2C2B34
:20448A002606046FA98A842BA9A81F767803280FFA680600A98800B6A61B00120C68A61BA2
:2044AA000C12096700530761A9280C12A69EA854026801B6A7920600BDB2BDAABDA202FEE5
:2044CA00A85BA58BA4864B9303524197C12B146106520F60A31B00104868A31B0310456648
:2044EA00A3924076CC9D00524260BF56C1033F6FBF56C1013C6FA3924076D79F00523C6012
:20450A00A38820FF0010A60F0460419210522061A38820FF1010A60F136041920C5210682D
:20452A0078520E66AC28ABAAA936AC2813002256A92D0C354193A888A90EA60F0961A388B0
:20454A0020FF0011A60F116041927C520E60419200D12061A192A3944076A69E9A9601D959
:20456A004192A154F866166FBF56C102C27B4193029ACA970F6F419200D10C61A192A394A7
:20458A00A90800EE407654979A9601D94192A154F66682FEBE82BE86BE8B0600BDB2BDAA7A
:2045AA0002FE1F767C03219225611F76800108421A6D0A420A6D1F767C03FABE0A061F7689
:2045CA007C032E03A60F726904021F768001061E0A426C6C1F767C03212B1E2B1F7680016E
:2045EA000A1E0D6F1F767C030402212B1F768001081E1F767C031E921F5423661F767C0397
:20460A001A921C610876303BBE5A1B59AD5D1C93008F40DD1D88A1CCFF0082DD1A2BA55DD6
:20462A00417E4076E99F1F767C03A1CD00FF1F2B42CB1E962097BD7A00761E921F543669C1
:20464A001F7680010406A940316D1F767C03008F40DD1F581F59012904B601D89C2504BEBA
:20466A0094AF5AFF1F92C0FF1F768001A908001F08C3A9251F767C030AC51F767C0320AFDF
:20468A001F7680010AC21F767C031F0802001F768401161E1F767C03BF5621012EC31F76D2
:2046AA00840114A91F76800104C282FEBE86BE8B0600BDAA34FEA892FF9C0564105204641E
:2046CA000F9A026F009A22761F76BE01AB28F0FFAA28457F09CDFFDF013B08B6A81A001058
:2046EA00A4A908BE099700D209CDFF3FA81A00400997A9851F7680015AFF2AC35D56A40094
:20470A00A0A9AB2BAA28467F08D42CC2ACA91F7684015AFF5D56AC0002A8A5A9AB28E0FF3E
:20472A00AA28457F0AC3AB03AB28FAFFAA28FFFF12C21F76800107BE00AA1F768401003A15
:20474A0008A0101E1F76800102A900C200BE0002011960C2601A80F2081E000260C40119A8
:20476A000C1E000201191E1E00020119221E00020119181E14C21844FF6C16065E1E5E1854
:20478A00FFFD5E1A00015D1800FF5D1A05005E1A7F005E18FAFF5EC416C26018FFEF60067E
:2047AA00141E1844FF6D0002488F70641F768301541E088F7064541A010000A00031531AA5
:2047CA000200010200BE02A8541A06001F768001408F8AB400D4301E54C5020232A82EA9DB
:2047EA00008F5FB41F76370008A01F768001241E26C220C31F7637000AA81F763300321AE4
:20480A001000321A2000237600011A761F767C0369FF000200B6182B261EB4FE22C224C3B0
:20482A00BE860600BDB2BDAA08FE00021F767D030ED03E1E9406481E4792A9800CD094C4C1
:20484A0020FF001FA70F11600AD094920F9008524C60A6061F767C03A80E48931B971C968F
:20486A001D7EBF561A01416F4806A80EA95AA9CC008000D14396A292A9CC00404296A29218
:20488A00A9CC00204196A218FF1FA606A80E489346974596447EAD5C83DCA45C9C92156107
:2048AA00A292805212677F520A61AD5C86DCA45CA2589C92008FC0DB9496076FAD5C86DC6B
:2048CA00A45C9C924076B5A501D9A19201DA0352E3684806455C445DA80E46934076879F0D
:2048EA001F76BF010F1A010088FEBE86BE8B06001F767C031992A88809611F768001080699
:20490A00A940046D0A06A9403E6C02D5A692013BAB28010001B6A572AA2B1F768001A9A867
:20492A00A581A98308A90AC301B6C58500915AFFA692A772A9A8A781A983C5853FFFABCABF
:20494A00AACBA71EA9A8A681A98A1F768401C48500915AFFA6853FFFAACBABCAA81A00E026
:20496A00061E04C322761F76800100022E1E1A760102041E1F76BF010E1A01001F767C03FF
:20498A00BF56190169FF0600C4A0D41A01000600C4A0D41A02000600D493009A03910053D5
:2049AA000260019A06001F76BF010118F7FF06001F76BF01011A08000600227608D0408FC3
:2049CA00806F1F76BE0195CCFCFF015008960BDD08CCF7FF0450089608CCDFFF10500896EB
:2049EA0008183FFF1F76BF01C51A08000118F7FF1A761F76C10169FF0002BF560007BF5682
:204A0A00011F022BBF56040C0A2800800B2B0C2BBF560F10D42BC41E001A800006001F76B1
:204A2A007A032388BDB2A48BA6921352E6FF5801C0761EC10129A7060456A601A71EC7C58D
:204A4A002076C18A240ED40F0561BF562311EFFF470108D091C5673E1F767A03232B242BEF
:204A6A00D118F7FFD118FDFFEFFF3A011F76C1010246ECFF3501C18A1F767A03240EC40725
:204A8A00A71E1F76C10107921F767A03C796BF562313240AEFFF24011F76C1010246ECFFD2
:204AAA001F0107921F767A03BF5623112596EFFF17011F76C1010246ECFF120107921F764C
:204ACA007A03BF56230F2596EFFF0A011F76C1010246ECFF050108D091C5673E1F76C1019A
:204AEA0007400B6DD118FBFF1F767A03D118FEFF232B242BEFFFF4001F767A03BF56230940
:204B0A00EFFFEE001F76C1010246ECFFE90007921F767A03BF56230B2596EFFFE1001F762F
:204B2A00C1010246ECFFDC0007881F767A03257EC18A240ED40F0561BF562307EFFFD00093
:204B4A0008D091C5673E1F767A03BF562309EFFFC7001F76C1010246ECFFC20007921F76F6
:204B6A007A03BF5623072596EFFFBA001F76C1010246ECFFB50007921F767A03BF562305E5
:204B8A002596EFFFAD001F76C1010246ECFFA800079208D01F767A03259691C5673E1F76B5
:204BAA007A03BF562303EFFF9B00D140086CBF5623010AD0D11A040091C5673ED141ECFF2B
:204BCA008F001F767A030AD0BF56230DD11A080091C5673EEFFF84001F76C101BF56008FB0
:204BEA00082B021A20001F767A03BF562312776F1F76C101BF56008FC18AE4920896021A84
:204C0A0020001F767A03BF562310696F1F76C101BF56008708280003021A20001F767A03C4
:204C2A00BF56230E5C6F1F76C101082B021A20001F767A03BF56230C526F0AD091C5673EA7
:204C4A001F76C101BF56008708280005021A20001F767A03BF56230A426F24801F76C101E6
:204C6A00BF56008FC18AA706C407A71EC79208961F767A03240A1F76C101021A20001F769F
:204C8A007A03BF5623082B6F1F76C101BF56008FC18AE4920896021A20001F767A03BF56F6
:204CAA0023061D6F0AD091C5673E1F76C101BF56008708280002021A20001F767A03BF56D8
:204CCA0023040D6F1F76C101BF56008708280006021A20001F767A03BF562302BE8B060027
:204CEA001F763000A880A927A48E142820400002008FB2DE1F767C03121E407659A20052B7
:204D0A00FD61A792A793C0FF0191A995A880A7922661408FA6DEAB9220BE1F90A674A792D1
:204D2A00A6540267A7881F767A03A65C0356AB01263AA90E28A82A1E008FB2DE407651A2C7
:204D4A00407659A20052FD61A93AA60DA98EAB92A694A927A792A69EA980DE601F7630002B
:204D6A001428104006001F763000A880A927A48E142820400002008FB2DE1F767C03121EA7
:204D8A00407659A20052FD61A792A793C0FF0191A995A880A7922661408FACDEAB9220BE4A
:204DAA001F90A674A792A6540267A7881F767A03A65C0356AB012C3AA90E2EA8301E008F6C
:204DCA00B2DE407655A2407659A20052FD61A93AA60DA98EAB92A694A927A792A69EA98071
:204DEA00DE601F76300014281040060002FE1F767A0336C5008FB2DE673E22761F763400E2
:204E0A00008FC7B51CA81A7669FF4076D4B41F7671030F8F40423A0642A8C00037B6A9BD1D
:204E2A00120F0077007702E84116008F10DF89E60000407613B52376002082FE0600009AD4
:204E4A00019C1952FE640600005206601F76BF010D1A0010056F1F76BF010B1A0010060090
:204E6A00BDB21F76BF01BDAA0D1A0008BDA2A95B010240762BB700D1808DC06FA28AA39262
:204E8A00CEFF0BDCC41A00804076B1A4A39280FFA95B010240762BB70DDA0102C21A008052
:204EAA0040762BB701D9A1921052E7641F76BF010B1A0008BE82BE86BE8B06000876303B58
:204ECA00BE931F7678032F92065206617FD0008FC0DB942B066F008FC0DB7FDCC418C00019
:204EEA00BD9700760600CC920461CC0B0260D42B0600A980A8881F767D031C0EA70F1A610E
:204F0A00008FC06FA4831F76BF010DDDC51A00400D1A00041F767D031C92015204600BDCB8
:204F2A00C41A00401C92025205601F76BF010B1A0004A6881F767D031D0EA60F24611D920D
:204F4A0012601F7678032F9206520861045206611F76BF010B1A0020166F1F76BF01021AF6
:204F6A000800116F1F7678032F9206520861045206611F76BF010D1A0020056F1F76BF01EB
:204F8A00041A08001F767D031B0E181E06000876303BBE271F7678032F921F767D031C880F
:204FAA001D9306520A6012920861008F5ADF407600A5A692407606A5BD3F00760600C496DA
:204FCA00CC2BD42BDC2B06000876303BBE931F7678032F92065224607FD0008FC0DB94923E
:204FEA00008F5ADF40766CA5008FC06FA4830DDDC51A00402F921F76BF010D1A0004065291
:20500A000761045205610BDCC41A0020036F021A080000BE1F767D0318C2BF561201BD97BE
:20502A0000760600A988C4925EFFC47EA6CEA646096DA9470D6CBF56D401CC97BF56DC0146
:20504A00076FA946056CBF56D402CC97DC2B06000876303BBE271F767D037FD0008FC0DBB9
:20506A001C2F1D801F76780394962F9306530F601F767D0312930B61A828E803008F5ADFD1
:20508A0040769FA5AA92A793407606A5BD3F007606001F767D03BF56100511287017008F2F
:2050AA0059021F767803BF561E3C1F767803BF561FA01F7678031D2870171F767D0316A879
:2050CA000600BDB2BDAABDA202FE79D0008FC0DBAC1E013BA98B94854C82A61E4456A900F0
:2050EA0042A2C00037B678D0A98342A2215694004456A100C00037B67FD0A607A503944949
:20510A00046C7AD0013B9485A918FF0FA95AA91A00804076BDA47BD0A9B2408FC0DB34FF07
:20512A00957AA61EA9B242A232FFA607C00037B6A98A42A2C802AC1EA9B24456A900C000BF
:20514A0037B61CD0A49E82FE289C9596BE82BE86BE8B0600A92802904076BDA41F7678035E
:20516A002F9206520F6104520D61025206611F76BF010F1A00040A6F1F76BF010F1A004064
:20518A00056F1F76BF01061A0002060002FE1F767D031092130A13546F661606132B4C60FE
:2051AA007FD0008FC0DB9447176C1192C0FF14540267140A1192140A145402671496C4E2DB
:2051CA001400C4E211014FE800C84FE80980007710E74000086F1592142B90E5019CA9185C
:2051EA00FF0315968EE6000014801188A9BF120F15931F7678031C7F1F7678031D7E1F763B
:20520A00780320960D617FD094480A6D1F767D031188013BA98542C24076EEA52D6F40768C
:20522A0037A61F767D03008FC0DBBF5615017FDCC418FFFE216F16060119A61E161E20FF0C
:20524A005802A60F136120FFF401160F0F6120FF9001160F0B6120FF2C01160F0761C80234
:20526A00160F04616402160F0760407637A61F767D03BF56150182FE060002FE22761F7622
:20528A0078032F9204520460008FAFAE036F008F92A81F7634001AA81A760F8F40421F7617
:2052AA00710369FF3A0642A8C00037B6A9BD120F0077007702E8D123008F08DF89E60000F9
:2052CA00407613B51F7630000C2820402376001082FE0600BDB2BDAABDA2407677AE00D3DB
:2052EA00103B69FF407661B6267600002F76000040760FB7407643B5407696941F767803C9
:20530A00009A2E934076E6A00D9A40767E91418F00C201D4A9A04076CD90418F00C202D455
:20532A00A9A04076CD90407683A44076FE93008FC06FA48300D20BDC0CDDC51A0100C41A9E
:20534A000100012920FF50C3407632B5008FC06FA48301DAA2920EDC0FDDC51A0100C41AC1
:20536A0001000A52EF64008FC06FA4830BDC0ADDC51A0100C41A01004076CC934076C29FD5
:20538A001F767803109B2E92407626B74076CAA6407680B61F7678032F92045205614076A0
:2053AA00FEAF40763784407663854076149A4076D3831F7678032F92065203604076D6A5FA
:2053CA00407671A51F767803C076C0DB2E927EDFC79622761F76C001BF56292F1A7669FF49
:2053EA0040765BA01F765C0305926761052B00D2008FC0DB013BA9A8A281A98A60D0948844
:20540A00A6925761008FA4DFA9A8A281A98AA692C40AC4544E66408FBEDEC42BA9A0A28141
:20542A00A98AC49280520868008FBEDE013BA9A8A281A98AC42B013B086F109AA794A9CC8D
:20544A00F0FFC4968052F167008FBEDEA9A8A281A98AC4800356A203408FF0D8A61EA79227
:20546A00C3FFA927A606AB0D0156A500A7CD0F00A82DC59262FFA988E161013B0A6F008F69
:20548A00BEDEA9A8A281A98AA692A0FFA988C40AA640F66C408DBEDE013BA9B2A281A98B4A
:2054AA00008FC0DB009AC193407605A2C108030001DAA29202529D647FD0008FC0DB9441EF
:2054CA00066C7FDCC418FDFF407604997FD0008FC0DB9442066C7FDCC418FBFF40765D972E
:2054EA007FD0008FC0DB9443066C7FDCC418F7FF4076149A7FD0008FC0DB9445066C7FDC30
:20550A00C418DFFF40765C804076B3951F7678032F920152136016D0408FC0DB9592A58AFA
:20552A00C1FF01F016D0A3CE0356A9027FDC0790C49895CC0400C1FFA95B40765B9F00D25C
:20554A00A2920461408D40D0036F408DC0D3A18A4076579CA91BFFFF53611F76BF0100BE9C
:20556A000F1A01001F767D0337523EC2116237523061335208623352386103523E61065273
:20558A0038613E6F34522D6135522761396F3A5208623A5212613852186139521261306F5B
:2055AA003B5207613C522C60A18A4076AF9C286FA18A4076569C246FA18A4076139C206F8A
:2055CA00A18A4076169B1C6FA18A4076529B186FA18A4076E99A146FA18A4076EA9A106F84
:2055EA00A18A4076649A0C6FA18A4076CB9B086FA18A4076B097046FA18A40761D9801DA7D
:20560A00A2920252E3FFEDFE9C6F1B76F0FF0500BDABBDA8BDA0BDC2BDC300E2BD0003E2EE
:20562A00BD0003E2BD0103E2BD0203E2BD0330E6000602FE69FF42291656227601021F762C
:20564A007C0301560A00237600102676391110291A767FD0008FC0DB944F046D69FF40761D
:20566A0052AD1F765C03040A049214520468042BBF56050116D1008FC0DB7FD09C9294CA81
:20568A000190A9880C9301F10191A6CB10601F7678032F93065306611F76BF010B1A000826
:2056AA00146F1F76BF010B1A00200F6F1F7678032F93065306611F76BF010F1A0008056FB9
:2056CA001F76BF010F1A00200653E1FF4B011F765C03120A12930A53E0FFA1001F767803FB
:2056EA002F931F765C030553122B0360189322601F7678032F93015305601F765C0317939C
:20570A0019601F7678032F9306530F6104530D61025306611F76BF010B1A0004226F1F7646
:20572A00BF010B1A00401D6F1F76BF01021A0002186F1F7678032F9306530F6104530D6154
:20574A00025306611F76BF010D1A00040A6F1F76BF010D1A0040056F1F76BF01041A0002E5
:20576A001F765C0316930F601F7678032F930353136101531161065319601F76BF01021A6E
:20578A008000186F1F7678032F9303530F6101530D61065306611F76BF010B1A00100A6FDB
:2057AA001F76BF01041A8000056F1F76BF010D1A00101F765C03179315601F7678032F9307
:2057CA0006530B610353046101531561196F1F76BF010B1A0040226F1F76BF01021A8000B1
:2057EA001D6F1F7678032F930653146103530D61015306611F76BF010B1A00800F6F1F76E7
:20580A00BF010D1A00800A6F1F76BF010D1A0040056F1F76BF01041A020000523E601F7674
:20582A005C03128062D0940EA70F37601F7678032F9303530F6101530D61065306611F769D
:20584A00BF010D1A00100A6F1F76BF01021A8000056F1F76BF010B1A001006530E610353C1
:20586A000961015304610D1A0080096F0B1A0080066F0B1A0040036F021A800006530B618A
:20588A0004530961025304610B1A0004066F0B1A0040036F021A00021F765C03009A1993B6
:2058AA0000530260019AA693A9CF04611128F401132B110AC4E21100197E12E8D01F14ADF9
:2058CA005764130A1393019100B6112B149713930791026101B6A692157F3C601F76780341
:2058EA002E92095224611F765C0316D0BF56160194937FD0D0FF9492C9FFA8CAA940026CFC
:20590A00167F0C42116D0C430C6D0C44036D172B2F6F1592009B00520260019B1797286FDD
:20592A0014921796256FBF561701226F1F765C030C41BF561801036C1492026F009A0C40DD
:20594A001796036D162B146F14931492189716960F6F14931488149216971896177E086FE6
:20596A0069FF407654A5407653A64076489E82FEAFE2BE03AFE2BE02AFE2BE01AFE2BE00F9
:20598A0080E2BE00BEC5BEC4BE83BE8ABE870300303BF1FF177602761B76F0FF0500BDABBA
:2059AA00BDAABDA8BDA0BDC2BDC300E2BD0003E2BD0003E2BD0103E2BD0203E2BD0330E672
:2059CA00000602FE69FF422916561F7633002292419623760100267601012218FA00212870
:2059EA00FFFF10291F765C030E9202610E0B1F765C030D92E0FF38011F765C030F920D61A8
:205A0A00FF9C0F960A60008FC0DBA4837FDC7FDDC518EFFFC41A04001F76780333921961CE
:205A2A0000D1008F0871013BA9A8A181A98AD492C3FFA988008FBCDDA9A8A181A98A69FF12
:205A4A00A192C47E4076018601D9A1920452EA641F7678032F92065237601F76C401099224
:205A6A00C3FFA90EA9BD120F0077007763D0808DC0DB8BE60000008F40D78CE6010069FF61
:205A8A0013E29201407600B66ED16FD0013B008FC0DB9A92929EA985A9BD160F6FD0C8E226
:205AAA009402007720E7800089E60900407662B350E800C8007790E8008800778CE60000A5
:205ACA001DD013E292001F7678032F920552406000D11F765C0307921F7674033D0A3D543E
:205AEA0002623D2B008F0871013BA9A83D81A98A3D2DC492C3FFA90EA9BD120F69FF008F93
:205B0A0040D712350156A4008BE60000407600B6013B1F767403008F86DE8EE60000A9A845
:205B2A003D81A98A13E2C400008F40DFA9A83D81A98A01D9C492085205613D924076E482E6
:205B4A00046F3D924076B780A1920652C3641F7678033292E1FF89001F76740301293E06A3
:205B6A0040FFA61E013B3C85A60F39691F767803329200D13465008F0871013BA9A8A181CF
:205B8A00A98A1F7674033D2D1F767803C492C3FFA90EA9BD120F3212A194A92D008F40D7F7
:205BAA0069FF8BE6000012350156A400407600B61F767403A192013B008F86DE3D2D1F76E7
:205BCA0078033233AB72A9A8AB81A98A01D98CE60000A19213E2C4003254CE641F76740312
:205BEA003C0A3C92FF9CA9853E0F3E681F767803329200D11F7674033C2B11651F76740331
:205C0A003D2D1F76780369FF3212A1944076B78001D91F767803A1923254F1641F765C0346
:205C2A0006921F767403029C3D0A3D5402623D2B1F76780330931F7674033D9200530F6103
:205C4A001F765C03065402600D9A1F7674033D931F765C03069F015302600C9A69FF4076F4
:205C6A002094026F0D0B008F0071A4831F76330019DC01DDC51A0040C41A1000BF562101D7
:205C8A00103B419282FE2296AFE2BE03AFE2BE02AFE2BE01AFE2BE0080E2BE00BEC5BEC43D
:205CAA00BE83BE8ABE86BE870300303BF1FF17760276005206601F76BF01041A0001056FC5
:205CCA001F76BF01021A00010600005206601F76BF01041A4000056F1F76BF01021A4000B2
:205CEA0006004076E6AB019A4076F2AB009A4076E6AB010240762BB7009A4076F2AB1E02D6
:205D0A0040762BB70600009A4076F2AB1E0240762BB7019A4076E6AB1E0240762BB7009A02
:205D2A004076E6AB1E0240762BB70600BDB2BDAABDA20EFE00D2A98BA288A492A52D025287
:205D4A00A65BE3FFF000A9B2026301D256FFA983AC9205640452036301D3026FAC2BAC9295
:205D6A00A58B4B2B0B650A02421EA9B2C00037B64B0AA98BAC924B54F762A9B20A650A02FA
:205D8A00421EA9B2C00037B601DE58FFA98BF862A292A58BA694A988026001BEA692AC9465
:205DAA004B2B0452A9880F650A02421EA9B2C00037B6AC80A98B4B0AA69281DFFC9CA72D3B
:205DCA004B54F362AC920263AC2B019A66FFA9580152026000D08076E803A9B242C2C000C5
:205DEA0037B6441EA9B242C2C00046B6A61E6402421EA606C00037B6461E6402421EA9B2C7
:205E0A00C00046B6A61E0A02421EA606C00037B6481E0A02421EA9B2C00046B64A1EA61E1E
:205E2A00460648074407A607046000D0A02DAC5AA3921561039A013B4B96AD888ADEA6888E
:205E4A000356A9010156A600C6060960AC924B5406610F024B0BC61E4B92F062A292206190
:205E6A00019A013B4B96AD5D8ADDA55D0356A9010156A5000F02C50F09614B920352066106
:205E8A00019C04524B96F0640B6FAD5D8ADDA55D0ABE4B920356A9010156A500C5C2009A1E
:205EAA004B2B013BAD5D8ADDA55D0356A9010156A5008076B0DAA606C507A61EC685C51ECA
:205ECA00019A4B2D66FFA0CE046180020156C5004B0A4B920452E76448A3AB92AA2BFF9070
:205EEA00A927A9A9AC28180037FF5AFF4A064E1E4E92FF904E964D2B4E06ABCAAACB4E1ECA
:205F0A004606009BFF903FFF5AFF4E06ABCAAACB4E1E44063B565AFF4E06ABCAAACB4E1EE2
:205F2A00026F4EB2408F8EDF013BA9A0A481A98A4B2BC45BA3921052246200D14E92013B2E
:205F4A000190A9884E0640FF4E1EA6924076FEAB01D9A1920852F364A35A00D1A293A292EA
:205F6A00B0FF0190A85A4076FEAB01D9A1920652F664407610AC01DB4B0A4B920452DB64A2
:205F8A008EFEBE82BE86BE8B060011D0008F107094920F9004520E60CCCC8001C6FF0252ED
:205FAA00096022761F76C001BF562555BF5625AA1A7669FF060022761F76C001BF5629687B
:205FCA001A7669FF0600BDB202FE1F76C001A888A9801143026C257611CC800105612276E2
:205FEA0011187FFE1A762276408F1070A58AA79211D00F9001DCC41A400095CDF0FFA8CAD4
:20600A0021961A76A79204600102A98B026FA759A92880C3A828C90169FFAC1E0556A1000E
:20602A00407668ADA1A91140FF6C22761118BFFF1A76A6930153036102530A602276A692F6
:20604A0011CD7FFE039086FFA8CA11961A76A6930A60A9B2013BAC281E0040FF2256A1078F
:20606A0041FFA98BA69301530A60A9B2013BAC281E0040FF2256A10741FFA98BA6930253C1
:20608A000960A9B2AC281F00013B2256A10740FFA98BA7921460A69203521160227611CC50
:2060AA007FFFA91A000169FF119620FFDA0540762BB71F76C001111A80011A761F76710354
:2060CA0082FE69FF3AB2BE8B0600BDB202FE22761F76C001BF561A011B2B1A92036001BEF2
:2060EA00046F03561A01A9881F76710342C269FF3A06C00037B63E1E1F76C0011B9203605A
:20610A0001BE046F03561B01A9881F76710342C23A06C00037B6408D1070A18315D0008FBE
:20612A00200B3C1E0CDD1F762C0094CCF8FF015014DC3596C41A04003418F7FFC51A0800B8
:20614A00787680001F76C0011C1A1000A18A1C1A00041C1A00081C1A20000DDC10D91C1A2A
:20616A0000011C1A00101C1A00201C1A00401C1A00801C18FBFFC41A01001D1A02001D1ACF
:20618A0004001D1A08001D1A10001D1A20001C1A04001D1A00041D1A00081D1A00101D1AE2
:2061AA0000201D1A00011D1A00021D1A00401D1A0080C11A0001201A0002201A0004201A86
:2061CA000008201A0010201A00201A7669FF82FEBE8B0600407668AD049A029B407670AD69
:2061EA004076F2AD060002FE22761F762B002028FFFF2128FFFF2228FFFF2328FFFF242878
:20620A00FFFF2528FFFF2628FFFF2728FFFF1A761F76FFCF69FF38924196399241963A9228
:20622A0041963B9241963C9241963D9241963E9241963F921F762B0041962F9201F001903C
:20624A0082FE06001B76F0FF0500BDABBDA8BDA0BDC2BDC300E2BD0003E2BD0003E2BD011C
:20626A0003E2BD0203E2BD0330E6000669FF4229165600D1227601021F767C0301560A008F
:20628A002376391110291A767FD0008FC0DB944F046D69FF407652AD1F765C03040A0492C6
:2062AA0014520668042B1F765C03BF560501008FC06FA4831F76780306DDC51A01002F9249
:2062CA0006520E6104520C61025205610FDCC41A00040A6F1F76BF010F1A0040056F1F7663
:2062EA00BF01061A00021F7678032F9206520861045206611F76BF010F1A0020056F1F76BC
:20630A00BF01061A080001021F767D033E07A61E008F204E3E1EA9A8A60F02673EA83B0A77
:20632A003B920A5202643B2B3792AC2B1261008FC0DBA9A8AC0DA98A62D094923B54046594
:20634A00019A66FFA172AC92019C3754A92DF0683A0A3A92025202653A2B7FD0408FC0DBA3
:20636A009593A89216D095CAA940EDFF8100A84B566D20FF204E3E0F2E693A920860013B7F
:20638A00CD8500D469FF01D5407623AC1F767D033A9201520A60013B1F766F0301D401D57E
:2063AA0069FF0085407623ACA192408DC0DB02D400D569FFD1CEA90E407623AC1F767D03C3
:2063CA003A9202527B60013B03D400D5D985407623AC746F3A92076000D4000200D569FFC4
:2063EA00407623AC1F767D033A920152076001D4000200D569FF407623AC02D4000200D52D
:20640A0069FF407623AC1F767D033A920252566003D4000200D5407623AC506F3A92086074
:20642A0020FFB82200D401D569FF407623AC1F767D033A920152086020FFB82201D401D582
:20644A0069FF407623ACA10E02D400D569FF407623AC1F767D033A9202523060012903D438
:20646A0000D520FFFFFF407623AC286F69FF4076AAB63A920760013B00D404D536854076F9
:20648A0023AC1F767D033A9201520760013B01D404D53685407623AC1F767D0302D400D59E
:2064AA003C06407623AC1F767D033A92025206603C0603D400D5407623ACAFE2BE03AFE21A
:2064CA00BE02AFE2BE01AFE2BE0080E2BE00BEC5BEC4BE83BE8ABE870300303BF1FF177675
:2064EA00027602FE2276008F59AA1F7635000AA81A7669FF4076FBB61F763300221A2000F1
:20650A00237601001F7678032F9206522360008F00711F76C401D4CCF0FF05500296DCCCAD
:20652A00F0FF0350039603CC1FFF1050039603CCFFF1A91A0005039603CCFF0FA91A004090
:20654A000396E4CCF0FF0750049604CC0FFF205004961F7678032F9203521460008F007186
:20656A001F76C401D4CCF0FF02500296DC18F0FF03CC1FFF1050039603CCFFF0A91A0002F2
:20658A0003961F7678032F9201522860008F00711F76C401D4CCF0FF06500296DCCCF0FF3E
:2065AA000550039603CC0FFF4050039603CCFFF7A91A0007039603CCFF0FA91A002003965C
:2065CA00E4CCF0FF0350049604CC0FFF6050049604CCFFF1A91A000104961F7678032F920E
:2065EA0002520960408F0071A58A02DCC418F0FFDD18F0FF2F9205524C60408F0071A58AA5
:20660A001F76C40102DCC41A0F00DDCCF0FF0250039603CC3FFF3050039603CCFFF0A91A21
:20662A000006039603CCFF0FA91A00C00396E5CCF8FF0A50049604CCBFFFB050049604CC24
:20664A00FFF7A91A0007049604CCFF0FA91A00400496EDCCF0FF0550059605CC1FFF105074
:20666A00059605CCFFFEA91A000E05960518FF0FF5CCF8FF08500696061AF00006CCFFFD86
:20668A00A91A000D069606CCFF0FA91A00900696408F0071C0760068A5C41F76C401A78AE3
:2066AA001AD019DD01DEC61A000119DC011A0008C51A1000001A10000018BFFF001A000F00
:2066CA00011A00401F763300BF5621011F76A001C41A000819CCFFFCA91A0004199697CC81
:2066EA00FFFDA91A00011A96BF56098000CC7FFCA91A0001009600CCFFE3A91A00080096D2
:20670A0000CC001CC9FFA92D019A66FFA98800CC800304600102AC1E086F00CC8003013B36
:20672A00C6FF80FFA985AC1E013BA6851F7671034456A900421E3A06C00037B6008FA60ED0
:20674A0042A8C00037B61F7678032F931F76A00105960018FCFF025303602502026FBB02D5
:20676A001F76740382FE3E1E06001B76F0FF0500BDABBDA8BDA0BDC2BDC300E2BD0003E2EF
:20678A00BD0003E2BD0103E2BD0203E2BD0330E6000602FE69FF422916561F76330032925F
:2067AA0041962376000126760001321875002128FFFF1029408FC0D369FFA58A40768A91B8
:2067CA0040768F91103B4192329682FEAFE2BE03AFE2BE02AFE2BE01AFE2BE0080E2BE0011
:2067EA00BEC5BEC4BE83BE8ABE870300303BF1FF177602761B76F0FF0500BDABBDA8BDA0AA
:20680A00BDC2BDC300E2BD0003E2BD0003E2BD0103E2BD0203E2BD0330E6000602FE69FFBE
:20682A00422916561F763300329241962376000126760001321871002128FFFF1029408FD3
:20684A00C0D369FFA58A40768A9140766092103B4192329682FEAFE2BE03AFE2BE02AFE2F1
:20686A00BE01AFE2BE0080E2BE00BEC5BEC4BE83BE8ABE870300303BF1FF177602761B7619
:20688A00F0FF0500BDABBDA8BDA0BDC2BDC300E2BD0003E2BD0003E2BD0103E2BD0203E264
:2068AA00BD0330E6000602FE69FF422916561F7633003292419623760001267600013218CF
:2068CA00FD002128FFFF1029408F40D069FFA58A40768A9140768F91103B4192329682FEAE
:2068EA00AFE2BE03AFE2BE02AFE2BE01AFE2BE0080E2BE00BEC5BEC4BE83BE8ABE87030056
:20690A00303BF1FF177602761B76F0FF0500BDABBDA8BDA0BDC2BDC300E2BD0003E2BD001E
:20692A0003E2BD0103E2BD0203E2BD0330E6000602FE69FF422916561F76330032924196A3
:20694A0023760001267600013292322B2128FFFF1029408F40D069FFA58A40768A91407658
:20696A006092103B4192329682FEAFE2BE03AFE2BE02AFE2BE01AFE2BE0080E2BE00BEC5D0
:20698A00BEC4BE83BE8ABE870300303BF1FF177602762276008F806FA483A4C41F76BE0141
:2069AA0026DDC51A030027DE261A0C00A483261A3000261AC000261A000317DD261A000C7D
:2069CA00261A0030261A00C0C61A0300271A0C00271A3000271AC000271A0003271A000C0A
:2069EA00271A0030271A00C0C51A0300A48328DD171A0C0016DC171A3000171AC000171A5B
:206A0A000003171A000C171A0030171A00C0C51A0300281A0C00281A3000281AC000281AF4
:206A2A000003281A000C281A0030281A00C0C41A00C0161AC000161A0030161A000C1A76CD
:206A4A0069FF06002276008F806FA4C4A4831F76BE0118DEC61A030019DD181A0C00181A86
:206A6A003000181AC000181A0003181A000C181A0030181A00C0C51A0300A48369FF191A7F
:206A8A000C0014DD15DC191A3000191AC000191A0003191A000C191A0030191A00C0C51AFE
:206AAA000300141A0C00141A3000141AC000141A0003141A000C141A0030141A00C0C41AAE
:206ACA000300151A0C00151A3000151AC000151A0003151A000C151A0030151A00C04076AF
:206AEA0056B20600227615D0008F200B1F762C0094CCF8FFA483A4C5A4C40150359614DD2A
:206B0A00C518FCFF0CDF3418F7FFA4830DDE341A0400C41A60000EDDC41A1C00C41A0300FE
:206B2A00C41A0030C41A000EC41A8001CC1A4000C41A0040C41A0080211A0300C71A6000D1
:206B4A002C1A1C002C1A03002C1A00302C1A000E2C1A8001C61A40002C1A00402C1A008083
:206B6A002D1A0300C51A6000A4830FDD2E1A1C0018DC2E1A03002E1A00302E1A000E2E1AB6
:206B8A008001C51A40002E1A00402E1A00802F1A0300C41A0700381A38001A7669FF407692
:206BAA0056B207F6007769FF0600BD3ABDB2BDAABDA202FE0129A9BF120F58FF5B61A85CEB
:206BCA007F91A8088000421EA493D6FFA85CA9BF160F6761A85D7F91A8088000A859A9585F
:206BEA00A593D6FFA85DA493A571A8180001A697A418FF00A518FF007FDCA492A59EA796A6
:206C0A004D64A90801FF3E62A193A09236FFA859A958420635FF0EF6A11FA95BA3010AF6E3
:206C2A00A11F2D56A204A32DA03640FF0BF6A11F33FF009B30FF54FFA20CA39540FFA70898
:206C4A0001001FF677FF200940FFA70801001FF677FF5AFFA7922265A90801FF1363A9A969
:206C6A0046FF7F91A85BA95AA625A79596FFA20CA395A9BD120F82FEBE82BE86BE8BBE8E0D
:206C8A000600009B57FFA8087FFF5AFFA693F260A8280080AA71AB92ED6F20FF0000EA6F65
:206CAA005AFFA493A818000196FFA85CA9A9A8087FFFA81C0080A4CBDD6F58FF5AFF00BEF3
:206CCA0025610129A6920190A958A60640FFA407A71EC793A640046DA892C7FFA92DA0921C
:206CEA0001520460A8CCFF00A92DAC58A592A0F201DEA092FF90A958A592408FC0DAC7FFB6
:206D0A009570A95DA9A9A60FDE66A5920600A71E8076C0DA0A6FA5928470FF90A95881DFED
:206D2A00A592C7FF9670A95DA706F660A59206000052A927FF76FFFF00BE3365A9A8013B88
:206D4A00A681A983A793C592FF90A92FAA71A880009AA740046DB0FFA880056FB0FFA81CEB
:206D6A0001A0A880019C0852F564A9A8013BA681A983A793C592C7FFA92FAA71A880009A04
:206D8A00A740046DB0FFA880056FB0FFA81C01A0A880019C0852F56401DEAB92A654CF6273
:206DAA00A79206001B76F0FF00E2BD0030E6000602FE422916561F7633003292419623767C
:206DCA000001267600013292322B2128FFFF102901021F767C03180A1F768001061E1F7662
:206DEA003300211A0001103B419282FE329680E2BE00303BF1FF177602761B76F0FF0500AF
:206E0A00BDABBDA8BDA0BDC2BDC300E2BD0003E2BD0003E2BD0103E2BD0203E2BD0330E6FC
:206E2A00000602FE69FF422916561F7633003292419623760001267600013292322B2128FF
:206E4A00FFFF1029008F006169FF40769FA11F76800102020C1E1F763300211A0001103B10
:206E6A004192329682FEAFE2BE03AFE2BE02AFE2BE01AFE2BE0080E2BE00BEC5BEC4BE8345
:206E8A00BE8ABE870300303BF1FF1776027600021F763000008F000C0119021E1F767C0348
:206EAA00408F100C000200A81F763000062B0119072BE41A1000041A20001F763000008F51
:206ECA00080C0A1E1F76300000020119121E1F76300000020E2B0F2B1F767C0308A810A0AD
:206EEA00021E1F763000162B172B1F763000E41A1000E51A10000C1A20001F763000141A05
:206F0A0020001F767C03121E0A1E060000E0E45003E2F40188E61000C48303E2D500C48321
:206F2A00F52BC483FD2BC483E51A1000C483E51A2000C483E518FFFBC483E518FFF7C4833D
:206F4A000002E51A0040D41E06000129BDB240FFBDAAA98B00D20861100240762BB701DABB
:206F6A00A9B2A20FFA66BE86BE8B06001F76C001201A0010407656B22276008F200BA4C4F0
:206F8A00A4831F762C000ED015DE14DDC618F8FFC51A03003418F7FF3418FBFF94CCBFFFE0
:206FAA0020502E960FDC2ECCE3FF08502E962ECCFCFF01502E962ECCFFDFA91A00102E9637
:206FCA002ECCFFF7A91A00062E962E187FFEC418BFFF2E18FFBF2E18FF7F2F1A03001A7629
:206FEA0007F6007769FF0600AD28000469FF1F5616561A5610E6000240291F7600000229F2
:20700A001B762276A9282FB7A828000001091B61C0762FB704290F6F009BA92401DF046CB1
:20702A000429A82401DFA61EA1F78624A706A1810109A71EA92403635CFF043BA95901DF1A
:20704A000900ECFF1A76A928FFFFA828FFFF01090E61FF76FFFF066F01DFBDC3A71E673EDA
:20706A00BEC5A92401DFA82458FFF7604076C7B64076E4B61B76F0FF0500BDABBDA8BDA02A
:20708A00BDC2BDC300E2BD0003E2BD0003E2BD0103E2BD0203E2BD0330E6000669FF4229CB
:2070AA001656227601021F767C0301561200008FB2DE69FF1F767A0338C5673E1A76AFE2E6
:2070CA00BE03AFE2BE02AFE2BE01AFE2BE0080E2BE00BEC5BEC4BE83BE8ABE870300303B94
:2070EA00F1FF17760276BDB2CFE6010008D003E2BD040ED103E2BD0503E2BD0603E2BD0717
:20710A00AFE2F400AFE2D40510E394040CD012E39C1F0ED118E394DA0AE3C4A000E3E4C639
:20712A0000E7BB0110E700010CD010E7C00003E29C0203E2940008D0F406941E03E2F401BD
:20714A00AFE2BE07AFE2BE06AFE2BE05AFE2BE04BE8B0600022904295F565AFF42065F5621
:20716A00421E00021FF617564200AB06325602292076022904295F565AFF420656FF421E82
:20718A0000021FF6175642003256022920765AFF00021FF617564200A9A920765AFF000274
:2071AA001FF6175642002076103B1F7633002018FEFF222B242B69FF262B282B2A2B2C2BCF
:2071CA002E2B302B322B342B362B382B232B252B272B292B2B2B2D2B2F2B312B332B352B0B
:2071EA00372B392B06001F763300201A01002128FFFF102969FF06000000A0E514AD096217
:20720A00A0E51F76740314AD90E5B4562901156F4FE803C092E601008CB5050000E7CA007B
:20722A00007700E78A000077CFE812F0007700E75100007700E74000060002FE1F767D03C4
:20724A00380AC4E23800007712E8D01F14AD1164390A3992382B0A520260392B3934570413
:20726A0036963C0603610002036F000201193C1E82FE0600A928FFFFAA28FFFFAB28FFFFB2
:20728A00A828FFFFAB0F04ED00D400BE0B6FA927A928FFFFA92FA4A9C488A928FFFF020975
:2072AA00A98AA6924076F7A606000077006F1F767E03BDB21EC5A959673E1F767E0322C50E
:2072CA00A70603ECA192673E1F767E03200603ECA71E673E4076E2B6BE8B060022761F76D1
:2072EA00C00169FF1C1A0800787680001A761F76C401028FE64969FFBF5618E0A9A8407689
:20730A002BB70600408F00C0008F000D2276009A85C4019C805284C2FC641A7669FF0600C2
:20732A001F767E031CA806001F767E031EA8060006001F767E031897199606000119C356CE
:04734A00FFFF06003B
:20734E001E01000000C0A78B0000A78B0000A78B0000A78B0000A78B0000A78B0000A78BE2
:20736E000000A78B0000A78B0000A78B0000A78B0000A78B0000A78B0000758C0000688CDE
:20738E000000F88F00009A8B00000F890000B18B0000828C0000458A00003A8A00002F8A05
:2073AE000000248A0000198A00000E8A0000038A0000F8890000ED890000668A00005B8A7D
:2073CE000000508A0000DD8A0000C28A00000F890000B9890000A089000005900000718A7F
:2073EE000000D2890000078E0000D18D00009B8D0000658D00002F8D0000F98C00000F893E
:20740E0000000F890000228E0000EC8D0000B68D0000808D00004A8D0000148D00000F893D
:20742E0000000F890000CF8E0000B48E0000998E00007E8E0000638E0000488E00000F8975
:20744E0000000F890000DE8C0000C38C00000F8900000F8900000F8900000F8900000F89D5
:20746E0000000F890000A78A00008C8A0000F28B0000BC8B00000D8C0000D78B00000F89C8
:20748E0000000F890000DD8F0000C28F0000A78F00008C8F0000718F0000568F00000F89BB
:2074AE0000000F890000A88C00008D8C00000F8900000F890000498B0000F88A00000F89BB
:2074CE0000000F8900007F8B00002E8B0000648B0000138B00003B8F0000058F0000208FA9
:2074EE000000EA8E00000F8900000F8900000F8900000F8900000F8900000F8900000F89DE
:20750E0000000F8900000F8900000F8900000F8900000F8900000F8900000F8900000F899D
:20752E0000000F890000858900006A8900004F89000034890000198900000F890000288C21
:20754E000000438C00000100010000000000EA07100A330B16006566353962642B662C0130
:20756E002C01000000000F00160014000A003C000A000800060008000600080033000B00E5
:02758E000A00F1
:20759000480000001EC162A30000EFA3000050A30000DFA3000043A30000D1A3000036A375
:2075B0000000BAA300001CA30000AAA300000FA30000A0A30000F3A2000093A30000E6A20A
:2075D000000085A30000D9A2000079A30000C3A20000AEA20000B89E0000BC9E0000519F87
:2075F0000000479F0000BF9E0000C29E0000C69E0000CB9E0000D09E0000D69E0000DB9EB0
:167610000000DF9E00002B9F00004B9F0000E29E0000579F0000BD
:02762600000062
:200400005B004F0066006D007D0007007F006F00400000000000000079000000FFFF81DFD6
:2004200000000600FFFF83DF00000000FFFF84DF00000000FEFF88DF000000000000FFFF93
:2004400055DF00000000FFFF56DF00000000FFFF57DF00000100FEFF9EDF00000DB70000C2
:20046000FEFFA0DF00000DB70000FEFFA2DF000000000000FEFFA4DF000000000000FFFF40
:200480003CDD00000000FFFF3DDD00000000FBFF90DF000008000C00100000000400FFFF9C
:2004A0009ADF00000A00FFFF9BDF00000100FCFF96DF00000000000000000000FFFF00D002
:2004C00000000100FFFF40DE00000000FFFF41DC00000000FFFF29DD0000000000000000E0
:2004E00017370000008002FEA4C5A927013B008FC0DBC7924196A9A8AB81A98A01BEC41814
:2005000000E04192C4984148046CC44E026D00BE008FC0DBA9A8AB81A98A1F765C03A6CC54
:20052000010088FFC4CDFFFDA8CAA993D8FF01F1C49601910B9982FE060010E70B0001E82E
:20054000EAFA20E740000DE8BA9E6FE819F000E7100000E7490000E70000007710E7400007
:20056000407672B602E8010250E8000040764BB30600A71EA59243880960C406056101025B
:200580004156C4000B6F00BE096FA706C40F036201BE046F01020156C400A6920600BDB26E
:2005A0007AD178D0013BBDAA808DC0DB9A92929EA985A9BD120F007700770077007789E601
:2005C000010002E8401640764BB37BD11F765C0379D0013B03E226009A92929EA985A9BD66
:2005E000120F007700770077007789E6010002E8401640764BB300D11F765C0303E22800C8
:20060000008FC0DB013BA9A8A181A98A74D0C8E29400008F30DFA9A8A181A98AC49202525E
:20062000046002E8D123056F02E8E1300AE8010040764BB3013B008F2ADD0356A10101563E
:20064000A40001D9A192045203E2C400DA64BE86BE8B0600BDB2BDAA03E2BD0404FE005249
:20066000A95906601F765C030B920B2B0C96A1921F765C0307540A63008FC0DB013BA9A803
:20068000A181A98AC44FEDFF0102008F86DE013BA9A8A181A9837FD0008FC0DB944CC4E236
:2006A000C500EDFFEA01A19207544F63A9A8A181A98AC44D4A6C00BEA6921F767D03A985B8
:2006C00000D5A71E009B009207520260019B0356A8011F765C030793A99F2765013B008FCD
:2006E000C0DBA9A8A581A98AC492A9CD0080A9CC0060A8CA0A60008F86DEA9A8A581A98A1B
:2007000001DEA706C40DA71E1F767D03009B009207520260019B0356A8011F765C0301DD4A
:200720000793A99FA555DC62A6920761A985421EA706C00020B6A71EA7BD120F00770077F6
:200740000077007789E60000A192089E1F767D03A988029242617FD0008FC0DB949316D060
:20076000D3FF9492C1FFA8CAA940146C1F767D03019205520C6002E849280CE8010094E6B1
:20078000080014AD0465A6920250A988A692E3FF6801A1921F765C030754E2FF8401A6401B
:2007A0000F6C79D0C8E29401007720E74000AFE2280400E72000007790E800760E6F78D08A
:2007C000C8E29401007720E74000AFE2260400E72000007790E800761F767D030392696181
:2007E000A692E3FFDE001F767D03019202521F61A1400F6C79D0008FC0DBC8E294011F76E2
:200800005C0320E74000AFE2280400E720003B6F78D0008FC0DBC8E294011F765C0320E718
:200820004000AFE2260400E720002D6F08BEA192A672008FC0DB013BA9A8A681A983807609
:2008400086DEA606A181A71E18D0C7929596A19206520D6378D0C8E294011F765C0320E7B8
:200860004000AFE2220400E720000D6F78D0C8E294011F765C0320E74000AFE2240400E79C
:200880002000007716E8000D14AD136512E8401A14AD0F63A12D008F00D950E800B212358F
:2008A0000156A4004076E9B502E8411640764BB316D0008FC0DB26E8000D94937FD0D0FF84
:2008C000949232E8401AC9FFA8CAA940036C02E8001150E80148A9A8013B18D08CE6000089
:2008E000A1818CE60900A983A9A8A181A98A13E29501432BA6BF120FC493A892CDFF01F0BC
:200900000190A980A61BECFF3461A692C852316130D09592FB9CA6540363A845186D008FD9
:20092000C0DBA9A8A181A98A30D0A69294540F6248D0945428651F765C03A7CC01000B18CD
:20094000F7FF82FF431A10000B981D6F4392A7CD01001F765C03205097FF0B18FBFFA91862
:20096000FFFEA9CB43970356A70207900B980B6F1F765C03A7CC0100431A01000B18EFFF9F
:2009800083FF0B981F765C030E92E0FF8C00AD5CA19283DCA45C40760080EFFF8400808DE3
:2009A000C0DB013BA9AAA681A98A8CE6000078D078D113E2940079D092929A9EA985A9BDEE
:2009C000120F007700770077007789E604000077CFE6210002E8081840764BB31F765C03A8
:2009E00003E2220002E8101CCFE621000FE8303340764BB37AD11F765C0378D0013B008FA4
:200A0000C0DB03E224009A92949EA985A9BD120F007700770077007789E6010002E80818C9
:200A200040764BB37BD11F765C0379D0013B008FC0DB03E226009A92949EA985A9BD120FF5
:200A4000007700770077007789E6010002E8081840764BB31F765C0303E22800236FA9A8A8
:200A6000013BA681A98A8CE6000078D013E2940040765C80176FA9A8A181A98A8CE600006D
:200A800018D013E294000E6F008FC0DBA9A8A181A983A9A8A181A98AC52B18D0C51A0080C2
:200AA000942B84FEAFE2BE04BE86BE8B0600BDB2BDAA02FE0052A92706601F765C030B9220
:200AC0000B2B0C96008FC0DB013BA9A8AB81A98AC492A94FEDFFCB00CDFF0190A92F422B81
:200AE000AB92075260610356AB01A95A1F767D034218FFFB008DC0DEA22D1006412B22563A
:200B00000190A95D0356A201A007A98A20FFE80340764680013B019B019089FFA2954298E0
:200B2000A82D4218FFF71006412B22560190A95D0356AC01A007A98A20FFE8034076468099
:200B400001908AFF4298AB9206522D60029A4218FFEFA294013BA92D1006412B22560190C8
:200B6000A95D0356AC01A007A98A20FFE80340764680013B039B0190A2958BFF4298A82D2E
:200B80004218FFDF10062256412B0190A95D0356AC01A007A98A20FFE80340764680019095
:200BA0008CFF42984292C0FF42CDBFFF42CA008F86DE013BC0FF42CAC0FF42CAC3FF40900D
:200BC000A8CA4296A9A8AB81A9837FD0008FC0DBC4E2C500944C416D408F30DFA9A0AB815D
:200BE000A9830356C501408F2ADD0156A500AFE2C50100E7400000778CE60000A9A8AB81F4
:200C0000A6BF120FA98318D148D0A6929D7E95541163408F30DEA9A0AB81A983421A08008F
:200C2000A9A8C581A98AC443056CAA920360421A000142920761AA9205601F765C030B1A80
:200C400002001F765C030E921E60AD5CAB9282DCA45C40760080176FA9A8AB81A98A8CE6FE
:200C6000000018D013E294000E6F008FC0DBA9A8AB81A983A9A8AB81A98AC52B18D0C51A4C
:200C80000080942B82FEBE86BE8B060000BE013B008FC0DBA9A8A681A98A01DE18D0A69234
:200CA000C41800E01852942BF46400BE013B008FC0DB408F78DFA9A8A681A98A0356A60102
:200CC0000156A50070D0C8E294001F767D0303E2C500019206521760408F30DFA9A0A6812B
:200CE000A983C59203520F60A640056D18D094288601096F008FC0DBA9A8A681A98A18D0F5
:200D00009428300201DEA6920452D264019201521060008FC0DB1F765C03A9A80781A98AC2
:200D2000C41A0040CC1A0040D41A0040DC1A00401F767D03019206520760008FC0DBC41A9C
:200D40000040CC1A00401F767D030692036140765C8006008076F9151F765C03408FF22BA0
:200D6000008F3E4900B61202BDB2AC2B013B1AC21EA020A81F767D0310C31F765C031C1E99
:200D8000008F30DFA9A8AC81A98AC42B008F30DEA9A8AC81A98AAC92019CC4211052A92DCF
:200DA000F0641F767D03019206521860008F30DF408F30DEBF56C4031F765C03BF56C50141
:200DC000BF56CC03CD2BBF56D402BF56D503BF56DC02BF56DD0210282C011F767D0301926B
:200DE00001521F601F765C03AC2B013BBF5610C8008F30DFA9A8AC81A98ABF56C403008F73
:200E000030DEA9A8AC81A98AAC92AC9301F002910190A8CAC496AC92019CA92D0452E96461
:200E20001F767D03009209523460008F30DF408F30DEC42BBF56C50108D0BF56CC010ED13E
:200E4000CD2BBF56D404BF56D503BF56DC04BF56DD02BF56E404BF56E505BF56EC04BF56C1
:200E6000ED04BF56F406BF56FC05BF56940609D0BF5694070AD0BF5694060BD0BF56940611
:200E80000CD0BF5694060DD0BF569408BF569C08AC2B013B90E50356AC01807660DF008F2E
:200EA0008ADF5AFF0156A600A9A8AC81A98AC6C3408F6CDFA9A9C42B0156A500AC92019C07
:200EC000A92D03E2C5000652E764AC2B013B00BE0356AC01008F42DC0156A400AC92019C95
:200EE000C4C2A92D1C52F564AC2B013B0356AC01008FC0DE0156A400AC92019CC4C2A92D57
:200F00002052F564AC2BC0762ED71235408F40D7008F00D90156A5000156A400AC92019C8D
:200F200011F68524A92D185211F68424EF64AC2B1235008FC0DD0156A400AC92019CA92DC9
:200F4000045211F68424F5641F767D030092A993FF9D01532269A993FD9D015315690752D4
:200F60000A61095222601F765C03BF56070FBF56080D1B6F1F765C03BF560608BF5607081B
:200F8000BF560808126F1F765C03BF560608BF560710BF560810096F1F765C03BF56060AAA
:200FA000BF560714BF560814BE8B06001F767D0305921F765C030780026104DF00D5013B03
:200FC00000D40356A5038076F0D8A4810156A60001DCA492C62B0852F56401DDA59202523C
:200FE000F0641F767D03019204526D61A79200D52D65013BA592A2FF008FF0D8CBFFA594C3
:20100000A3FFA927A9A8AB81A61E0356AB04A92D019BA592ACEA189A67FFA594C699A988F5
:20102000A2FFCBFFA594189CA3FFA92701DDA9A8AB81A98A0356AB04A92DA692ACEA019A11
:2010400066FFC498A792A554D66200D5013B008FF0D803BEA9A8A581A71EA592A672A9A860
:20106000A681A98A08D001DDC7929496A5920352EF641F767D030192008FF8D8025204603F
:20108000A9280003036FA928000FFC981F767D0305920961408FF0D8A58A0FDCC41AFF00F2
:2010A000F51A003F1F767D03019206521260408FF0D8A58A0FDCC41A3000FD1A00E0CD1AD3
:2010C0000030076F008FF8D8C41A0F00F41AFC01008FF0D8A48308DC01DDC51A0100F41AE5
:2010E0000700FC1A00E00600BDB2BDAABDA203E2BD0403E2BD0503E2BD060CFE1F767D03A4
:201100004496019206520860449206601F765C030B920B2B0C961F765C030792008F30DED3
:20112000013B4494A988A9A84481A98A808DC0DBC4924696449201F0019047964492A0FF9D
:20114000A92D499603354896049AAC94A95BA9AAA681A98A457EC44FEDFFBE02008FBCDD8B
:20116000A9A84481A98A64BE4492A672C4E2C405A9AAA681A98A8CE62800408D78DF13E247
:20118000C40044920356A9010156A1004C1E02E891340BE801E0AFE2C10020E72800407696
:2011A0004BB3442D008FC0DD1235AFE2C10110E740000156A40003E2C1004076E9B5013B92
:2011C0008CE60000A9AA4481A6BF120FA98A70D0408F2ADD947E4C060156A500A685A9BDC5
:2011E000120F007700770077007789E60000408D6CDF21E3C5414C0600E70C000156A10024
:2012000000E72101AFE2C10320E7C80002E8512B0BE8010040764BB3AFE2C10110E7400009
:20122000007703E2C100407672B6013B008F8ADF8CE60000A9A84481A98A13E2C400449235
:20124000A940ECFFAA001F765C03CFE62000AFE22A0140762A80452D008F00D912350156B3
:20126000A4004076E9B54488008F30DF013BA9A8A681A98A1F765C0303E22C00C492035275
:20128000086012E8000D14AD046390E503E22C00008F30DFA9A8A681A98AC49202520860D6
:2012A00012E8B01414AD046390E503E22C0001E8A9FDAFE22C0508E8992740764BB31F7678
:2012C0005C03CFE606000E921660008FC0DB4588013BA9A88CE6280018D0A681A98381DE26
:2012E000A9A8A681A98A13E295008CE6330018D013E29403013BCFE62000AFE22A01408D06
:201300006CDF0356A301AFE6000020E740000156A10000E70100AFE2C10020E7080002E87E
:20132000512B0BE8010040764BB3AFE2C10110E74000007703E2C100407672B6013B408FF9
:201340008ADF8CE60000A9A0A381A61EABBF120F68B6C63F4892A772008FC0DBA9A8A7813D
:20136000A61EA9A04681A71EAA286900C792C696A9A04481A71E4892AA72A9A8AA81A61EDB
:20138000AA286A00C792C6964892AA72A9A8AA81A98AC43F116F1F767D03019201520660D3
:2013A000CFE62000AFE60000036FCFE620001F765C0303E22A00008F8ADFA9A84481A9833F
:2013C000A9A84681A98AC8E2C400008F8ADFA9A8A381A98A4488C8E2C401008FC0DBA9A8FE
:2013E000A681A98AC8E2C505432BC492CDFF01F00190008F8ADFA959A9A8A681A98A96E6EC
:20140000050096E60D00C8E2C400CFE6290020E7280040764BB301E8C9F400D50CE8D1CC03
:2014200094E6080014AD0B651F765C03C8E21000007794E6050014AD026501D54492013B45
:20144000008F60DF1F765C030356A901412B0156A4001E064076468000520A614393A1CCC0
:201460000100045187FFA818FFFEA8CA4396008F8ADF013BA9A8A381A98AC8E2C400CFE689
:20148000290020E7280040764BB301E8C9F400D50CE8D1CC94E6080014AD0B651F765C038D
:2014A000C8E21000007794E6050014AD026501D5013B008F60DF1F765C030356A301412B17
:2014C0000156A4001E064076468000520A614393A1CC0100045187FFA818FFFEA8CA43968D
:2014E0001F767D0301920652606047925E60008FC0DB013BA9A84581A98A30D0C8E29400A7
:20150000007794E6060014AD0A654393A1CC0100205187FFA818FFFEA8CA4396008F30DFC3
:20152000A9A84481A98AC49203522760008FC0DBA9A84581A98A48D0412BC8E2940000D525
:2015400094E6060014AD026301D54992008F96DF1F765C030356A9010156A4001E06407664
:20156000468000520A614393A1CC0100085187FFA818FFFEA8CA4396008F30DF013BA9A892
:201580004481A98AC49202521060008FC0DBA9A84581A98A48D0C8E29400007794E6060078
:2015A00014AD0365431A10004492015204610352E0FF87004392E1FF84001F767803089269
:2015C0007F601F7678032A060C192C0F036728062C1E2C8A8CE62000A9A801092C1E13E2C8
:2015E000C4002C8A1F765C03AFE22A00A9A81F7678038CE6000001092C1E13E2C4002C8A2C
:201600008CE62B00A9A801092C1E13E2C4032C83013B008F8ADFA9A001092C1EA9A8448136
:20162000A71EC792C5962C83A9A001092C1EA9A84681A71EC792C5962C83A9A001092C1E08
:20164000A9A8A381A71EC792C5962C8A1F765C03A9A80109AFE22C001F7678038CE6000058
:201660002C1E13E2C4002C8A8CE63200A9A801092C1E13E2C4022C83008FC0DBA5C4A9A81A
:201680004481A71E01DE70DF2CC2C792C5962CC5A70601092C1E1F765C0310921F76780358
:2016A000C7962CC5A70601092C1E4392C7962CC5A70601092C1E1F765C030E92C7961F7631
:2016C0005C030E921560AD5C459283DCA45C407600800E6F008FC0DBA9A8A681A983A9A885
:2016E000A681A98AC52B18D0C51A0080942B8CFEAFE2BE06AFE2BE05AFE2BE04BE82BE8690
:20170000BE8B06001B76F0FF00E2BD0030E60006422916562576006F1B76F0FF00E2BD003F
:2017200030E6000602FE422916561F7633003892419623760008267600003892382B21289A
:20174000FFFF1029103B419238962576006F1B76F0FF00E2BD0030E6000602FE422916564A
:201760001F763300389241962376000826760000381890002128FFFF1029103B4192389677
:201780002576006F1B76F0FF00E2BD0030E6000602FE422916561F7633003892419623762B
:2017A000000826760000381890002128FFFF1029103B419238962576006F1B76F0FF00E2CD
:2017C000BD0030E6000602FE422916561F76330038924196237600082676000038189C00C7
:2017E0002128FFFF1029103B419238962576006F1B76F0FF00E2BD0030E6000602FE4229CD
:2018000016561F76330038924196237600082676000038189E002128FFFF1029103B41922A
:2018200038962576006F1B76F0FF00E2BD0030E6000602FE422916561F763300229241966B
:2018400023760101221868002128FFFF1029103B419222962576006F1B76F0FF00E2BD00CC
:2018600030E6000602FE422916561F76330022924196237601012292222B2128FFFF102906
:20188000103B419222962576006F1B76F0FF00E2BD0030E6000602FE422916561F7633008E
:2018A000229241962376010026760101221878002128FFFF1029103B419222962576006F53
:2018C0001B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF00E2BD0030E67E
:2018E00000064229165610292576006F1B76F0FF00E2BD0030E600064229165610292576E2
:20190000006F1B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF00E2BD00E4
:2019200030E600064229165610292576006F1B76F0FF00E2BD0030E6000642291656102926
:201940002576006F1B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF00E2C6
:20196000BD0030E600064229165610292576006F1B76F0FF00E2BD0030E600064229165662
:2019800010292576006F1B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF2F
:2019A00000E2BD0030E600064229165610292576006F1B76F0FF00E2BD0030E600064229AC
:2019C000165610292576006F1B76F0FF00E2BD0030E6000602FE422916561F7633002292CA
:2019E000419623760100267601012292222B2128FFFF1029103B419222962576006F1B76E6
:201A0000F0FF00E2BD0030E6000602FE422916561F7633002C924196237620002676390159
:201A20002C922C2B2128FFFF1029103B41922C962576006F1B76F0FF00E2BD0030E60006EC
:201A400002FE422916561F7633002C92419623762000267639012C1832002128FFFF1029CD
:201A6000103B41922C962576006F1B76F0FF00E2BD0030E6000602FE422916561F763300A2
:201A80002292419623760100267601012218F8002128FFFF1029103B419222962576006FF1
:201AA0001B76F0FF00E2BD0030E6000602FE422916561F763300229241962376010026768B
:201AC00001012218FA002128FFFF1029103B419222962576006F1B76F0FF00E2BD0030E63B
:201AE000000602FE422916561F763300309241962376800026760000301803002128FFFF61
:201B00001029103B419230962576006F1B76F0FF00E2BD0030E6000602FE422916561F76F7
:201B20003300329241962376000126760001321875002128FFFF1029103B419232962576E0
:201B4000006F1B76F0FF00E2BD0030E6000602FE422916561F763300329241962376000107
:201B6000267600013218FD002128FFFF1029103B419232962576006F1B76F0FF00E2BD00F2
:201B800030E6000602FE422916561F7633003092419623768000267600002C18230021288C
:201BA000FFFF1029103B419230962576006F1B76F0FF00E2BD0030E6000602FE42291656EE
:201BC0001F763300329241962376000126760001321871002128FFFF1029103B419232964A
:201BE0002576006F1B76F0FF00E2BD0030E6000602FE422916561F763300329241962376CD
:201C00000001267600013292322B2128FFFF1029103B419232962576006F1B76F0FF00E233
:201C2000BD0030E60006422916562376390110292576006F1B76F0FF00E2BD0030E60006A3
:201C4000422916562576006F1B76F0FF00E2BD0030E600064229165610292576006F1B76BD
:201C6000F0FF00E2BD0030E6000602FE422916561F7633002C9241962376200026763901F7
:201C80002C1837002128FFFF1029103B41922C962576006F1B76F0FF00E2BD0030E6000624
:201CA00002FE422916561F7633002C92419623762000267639012C922C2B2128FFFF1029CC
:201CC000103B41922C962576006F1B76F0FF00E2BD0030E6000602FE422916561F76330040
:201CE0002C92419623762000267639012C1833002128FFFF1029103B41922C962576006FDF
:201D00001B76F0FF00E2BD0030E6000602FE422916561F7633002C924196237620002676FF
:201D200039012C1822002128FFFF1029103B41922C962576006F1B76F0FF00E2BD0030E664
:201D4000000602FE422916561F76330038924196237600082676000038189F002128FFFFCA
:201D60001029103B419238962576006F1B76F0FF00E2BD0030E6000602FE422916561F768D
:201D800033003892419623760008267600003892382B2128FFFF1029103B419238962576FE
:201DA000006F1B76F0FF00E2BD0030E60006422916562576006F1B76F0FF00E2BD0030E663
:201DC0000006422916562376390110292576006F1B76F0FF00E2BD0030E6000642291656FE
:201DE0002376391110292576006F1B76F0FF00E2BD0030E600064229165610292576006F68
:201E00001B76F0FF00E2BD0030E6000602FE422916561F763300309241962376800026769A
:201E20000000301801002128FFFF1029103B419230962576006F1B76F0FF00E2BD0030E6B6
:201E4000000602FE422916561F7633003092419623768000267600002C922C2B2128FFFF33
:201E60001029103B419230962576006F1B76F0FF00E2BD0030E6000602FE422916561F7694
:201E800033002A92419623761000267610012A922A2B2128FFFF1029103B41922A9625761C
:201EA000006F1B76F0FF00E2BD0030E6000602FE422916561F7633002A924196237610009D
:201EC000267610012A1802002128FFFF1029103B41922A962576006F1B76F0FF00E2BD008A
:201EE00030E6000602FE422916561F76330024924196237602002676000024181F002128BF
:201F0000FFFF1029103B419224962576006F1B76F0FF00E2BD0030E6000602FE4229165696
:201F20001F763300269241962376040026763D012692262B2128FFFF1029103B4192269670
:201F40002576006F1B76F0FF00E2BD0030E6000602FE422916561F76330024924196237677
:201F600002002676000024180F002128FFFF1029103B419224962576006F1B76F0FF00E2B4
:201F8000BD0030E6000602FE422916561F763300269241962376040026763D01261826005F
:201FA0002128FFFF1029103B419226962576006F1B76F0FF00E2BD0030E6000602FE422917
:201FC00016561F7633002492419623760200267600002492242B2128FFFF1029103B419266
:201FE00024962576006F1B76F0FF00E2BD0030E6000602FE422916561F76330026924196B4
:202000002376040026763D01261826002128FFFF1029103B419226962576006F1B76F0FF01
:2020200000E2BD0030E6000602FE422916561F763300249241962376020026760000241846
:2020400008002128FFFF1029103B419224962576006F1B76F0FF00E2BD0030E6000602FEDB
:20206000422916561F763300269241962376040026763D012692262B2128FFFF1029103BE7
:20208000419226962576006F1B76F0FF00E2BD0030E6000602FE422916561F763300249217
:2020A0004196237602002676000024180C002128FFFF1029103B419224962576006F1B76D7
:2020C000F0FF00E2BD0030E6000602FE422916561F763300269241962376040026763D01B1
:2020E0002692262B2128FFFF1029103B419226962576006F1B76F0FF00E2BD0030E6000638
:2021000002FE422916561F76330024924196237602002676000024180E002128FFFF102992
:20212000103B419224962576006F1B76F0FF00E2BD0030E6000602FE422916561F763300E3
:20214000269241962376040026763D0126183E002128FFFF1029103B419226962576006F99
:202160001B76F0FF00E2BD0030E600064229165610292576006F1B76F0FF00E2BD0030E6D5
:20218000000602FE422916561F76330028924196237608002676080128180F002128FFFF2D
:2021A0001029103B419228962576006F1B76F0FF00E2BD0030E6000602FE422916561F7659
:2021C000330028924196237608002676080128180F002128FFFF1029103B419228962576AF
:2021E000006F1B76F0FF00E2BD0030E6000602FE422916561F763300289241962376080064
:2022000026760801281803002128FFFF1029103B419228962576006F1B76F0FF00E2BD0051
:2022200030E6000602FE422916561F76330028924196237608002676080128180300212880
:20224000FFFF1029103B419228962576006F1B76F0FF00E2BD0030E6000602FE422916564F
:202260001F7633002A92419623760800267608012892282B2128FFFF1029103B4192289654
:202280002576006F1B76F0FF00E2BD0030E6000602FE422916561F76330028924196237630
:2022A000080026760801281802002128FFFF1029103B419228962576006F1B76F0FF00E267
:2022C000BD0030E6000602FE422916561F76330032924196237600012676000132187500F5
:2022E0002128FFFF1029103B419232962576006F1B76F0FF00E2BD0030E6000602FE4229C8
:2023000016561F7633003292419623760001267600013292322B2128FFFF1029103B4192F8
:2023200032962576006F1B76F0FF00E2BD0030E6000602FE422916561F7633003292419656
:202340002376000126760001321831002128FFFF1029103B419232962576006F1B76F0FFDB
:2023600000E2BD0030E6000602FE422916561F76330032924196237600012676000132926D
:20238000322B2128FFFF1029103B419232962576006F1B76F0FF00E2BD0030E6000602FE35
:2023A000422916561F7633002E92419623764000267600002E922E2B2128FFFF1029103B8E
:2023C00041922E962576006F1B76F0FF00E2BD0030E6000602FE422916561F7633002E92C2
:2023E000419623764000267600002E1828002128FFFF1029103B41922E962576006F1B7626
:20240000F0FF00E2BD0030E6000602FE422916561F7633002E924196237640002676000067
:202420002E1820002128FFFF1029103B41922E962576006F1B76F0FF00E2BD0030E600068F
:2024400002FE422916561F7633002E92419623764000267600002E1838002128FFFF1029D3
:20246000103B41922E962576006F1B76F0FF00E2BD0030E6000602FE422916561F76330096
:202480002E92419623764000267600002E1838002128FFFF1029103B41922E962576006F46
:2024A0001B76F0FF00E2BD0030E6000602FE422916561F7633002E92419623764000267636
:2024C00000002E183E002128FFFF1029103B41922E962576006F1B76F0FF00E2BD0030E6D7
:2024E0000006422916562376390110292576006F1B76F0FF00E2BD0030E6000602FE422943
:2025000016561F763300229241962376010026760101221848002128FFFF1029103B4192A4
:2025200022962576006F06000052A888C48A096509520763FF9CC4CDF8FF0790A8CAC4964A
:20254000A69245520D614E5208614F520D60C41A2000C418BFFF086FC418DFFF056FC41A0C
:202560002000C41A4000A59201520360C4187FFF02520360C41A8000C418EFFFC418F7FF25
:202580000600BDB200D5013B008F40DEA9A8A581A98AC47D01DDA5924652F76433D1008F82
:2025A00040DE34D0BF569C0CBF56940C1F76400335D03BD1BF56940836D0BF56940937D02E
:2025C000BF56941038D0BF56940839D0BF56940C3AD0BF56940C3DD0BF569C053CD10092AA
:2025E000FE9C949641D00092FF9C94963ED0BF56940810D0BF56DC08BF56F408BF56940D50
:20260000BF56EC1BBF569C12BE8B0600A9BD120F00770077BDB21F7671038BE60100A48BFE
:2026200050E8094088E23C0040764BB388E6000000770077A9BF120FC18AA993B7FFFF910D
:20264000D497C18AFF90DC96BE8B06000852A927A4C50960089A01D54E9B40762190008D23
:202660003B03972BAB9209520B60089AA78A01D54E9B40762190008D3B03BF5697010600E0
:20268000A71EBDB2A492BDAA0152BDA22A60008F5070C08D40D01F76410300A822761F76D3
:2026A000BE01408FFAB109CDFFF7A81A0004008FB5B1099709CDFFFD1F763700A81A000159
:2026C00000A01F76BE0109971F76370002A81F763300321A0100321A0200237600011A7663
:2026E00002523360008F5077C08DC0D31F764F0300A82276408F806F1F76BE01FDCDFFEFCC
:20270000807670B1A58AA81A0020079716DD07CDFF3F1ADCA81A008007971F763700C5186F
:20272000CFFF408F2BB1C41A040004C21F763300321A04001F76370006A01F763300321ADA
:202740000800237600011A76D396A38A01D54E9B69FF089AC38640762190A2C4A2830BD0D2
:20276000A28A0CDE0CDDC61A00400ADCC518FFDFCA18BFFFCA18DFFFCA18F7FFCA18FBFF85
:20278000CA1A0200CA1A0100C418FFBF92CCE0FF0BD00150929640764E90A38AA70640761F
:2027A0009390D392025205601F76BF01081A0400C38A099ABF56E402A38A4076B390408D7F
:2027C00033030CD0932BA28A008D9E01932B9B2B0BDCC41A4000CA1A2000BE82BE86BE8B77
:2027E00006001F764103BF5602011F764F03BF5602021F764003009606000002008D42039A
:20280000941E0600BDB2BDAAA486008D3A0392923660A9AA13FF6700A98A008D36030102B4
:202820000156C4009206008D3803920F0466C28ABF56E40208D092C5A7C408D0090201DE6F
:2028400092C2C207A98A008D3603C792C4969206008D3803920F4966C28AE446FE6CA28AF3
:20286000099A4076B390D292025205601F76BF01081A0400008D9E01922B376F008D3803CD
:20288000928A408D3803A492008D36030190A98801DC9AA89206008D3803920F0466C28A80
:2028A000BF56E402A6920B6008D092830902C207A98AC592A7FFFF90C4960D6F08D0928337
:2028C000A5C408D0090201DE92C2C207A98AC592FF90C496008D36039206008D3803920F76
:2028E0000466C28AE446FE6C1F763300211A0001BE86BE8B0600BDB200BEACA0BDAAA486ED
:20290000BDA2A98201DEA61B3075FD69D292025205601F76BF010818FBFF00BE01DEA61B98
:202920001027FD69408D3603A9AB08D09AA20109921E008D3A03922BC28AE446FE6CA28A3F
:20294000089A4076B39001BE008D38030102A30F92C21C68A7AB0902C207A98AC792C496BC
:20296000C28AE446FE6C00BE01DEA61BE803FD69A28A099A4076B390D292025210601F7643
:20298000BF01009A081A04000B6FC28A0902A7ABBF56E401C207A98AC792C496009ABE8211
:2029A000BE86BE8B0600BDB2BDAAA486EFFF2A01C28AFCCCFF00A958EC47EDFF1D01408D4D
:2029C000A0019A92E0FF1E01408D3B039A920152116000BE408D3D03BF569A01408D3403B2
:2029E0009AC2408D3F039A2B408D3E039A2B0DD19A2B408D3F039A92E0FF0401408D3D0305
:202A00009A924F60F2C4208F0000A9A8A60F06662F8F0000A9A8A60F0867608F00000AD107
:202A2000208F0000F2A09AA80CD19A923A52E0FFE900408DA0019A922E60408D34039AC42C
:202A4000A692A94001DE9AC2066DF28A0356A008C496076FF28AA9A80109F21EA092C498E5
:202A6000008D34039206E20FE8FFCC000FD10CD0BF569A33A28A099ABF5692334076B39016
:202A8000408D3F03008DA001BF569A01BF569201EFFFB800008D3F03BF569201EFFFB200E4
:202AA000408D3E039A923E60A0921F760403115425611F767E031B5403601B921F60A08849
:202AC000408D31039A0EA60F0560408D33039A921560A0921F767E031A5407601A92056160
:202AE000408D33039A920A61408D3F03008D3D03BF569A01922BEFFF8500408D32039A786C
:202B0000408D3E039A80019A013BA7949A96A9AAA781A98A0ED1089A9C78A28A4076B390E3
:202B2000706F9A80019A013BA7949A96A9AAA781A98A0ED19C78408D3E039A9207520E60B8
:202B40000DD19A9210520A60408D33039A9206600A9A1F767903A0941096408D3E039A9241
:202B600002521A60A0920DD103529A78336842523166008F40DE949203522C68A0923352D7
:202B80000B600CD19A923A522560A09233520460008D3D03922B0DD09258408D3E03008FA7
:202BA00040DE9A9294540467A91B90012A68A28A099A4076B390408D3D039A2B0DD0408D4D
:202BC0003F03BF569A01408DA001BF569A01922B186FA28A099A4076B390408D3E039A2B06
:202BE0000DD0408D3D03BF569A01408D3F039A2B922B076FC28ACC18DFFFC28ACC1A2000D4
:202C0000C28AEC46EDFFD6FE1F7633000BDC211A0001C41A4000BE86BE8B0600BDB2BDAA04
:202C2000A486A61EBDA2A582D292025205601F76BF010818FBFF408D360308D09AC292A226
:202C4000008D3A03BF569201C28AE446FE6CA28A089A4076B390008D38030102921EC2839B
:202C600000BE0902BF56E501C207A98AC392A7FFFF90C496A692BE82BE86BE8B060022760D
:202C80001F767D0316D0008F806F079294CDFCFF03901F76BE0118D0A8CA16961F767D03C4
:202CA000089294CDFCFF03901F76BE011AD0A8CA18961F767D030992019094CDFEFF1F76F9
:202CC000BE01A8CA1BD01A961F767D030A9201901F76BE0194CDFEFFA8CA1B961A7669FF19
:202CE000060016D0008F806F1F767D039492039018D00796949203901AD00896949201901F
:202D00001BD00996949201900A9622761F76BE011618FCFF1818FCFF1A1A01001B1A010027
:202D20001A7669FF06001F767D03019302531660008F880A1F76BF01A94002A8036C041A8B
:202D40000008A941036C041A0002A942036C041A8000A943036C041A080003533B60AB28B5
:202D6000CB0BAA2800091F76BF01248F0000005202A90AA80360041A000801520360041A8E
:202D8000000202520360041A800003520360041A080004520360041A020005520360041A4D
:202DA000000106520360041A400007520360041A010008520360051A000809520360051A5D
:202DC00000010A5203600D1A20000B5203600D1A040001531E60AB2BAA28000B1F76BF0127
:202DE000128F0000A94004A90CA8036C031A0008A941036C031A0001A942036C031A000264
:202E0000A943036C0B1A0200A944036C0B1A1000060022761F76BE01408F806F06180000D1
:202E20000F8F00F0A5C4071800FF08183F00091800FF1BDD0BDE1606A918C0FF161EA9A8FC
:202E400018C0A89219C0C618EFFF0B18DFFF0B18BFFFC518F7FF1A76008FC06F1F767D03A3
:202E6000BF56000109D0CC92C4FF01F0019000721F76BF010192C3FF1F767D0301F001900D
:202E800080FF00721F76BF010192C5FF1F767D0301F0019081FF00729492C2FF01F00190A3
:202EA00082FF009400960352036104520360BF5601010152036102520360BF560103055200
:202EC000036106520360BF56010607520360BF56010208520360BF560104095209610192B4
:202EE000015208610552066106520461056FBF560105BF56060101920152036106520360EB
:202F0000BF5605010252036103520360BF5603010152036105520360BF56020101520361CD
:202F200003520360BF56040202520360BF56040103520F6101520D61065206611F76BF0153
:202F40000D1A00100A6F1F76BF01021A8000056F1F76BF010B1A001006520E61035209614C
:202F6000015204610D1A0080096F0B1A0080066F0B1A0040036F021A800006520B610452D3
:202F80000961025204610B1A0004066F0B1A0040036F021A000206520661045204610B1ADC
:202FA0000020036F021A0800227604520A6204522461015240610252346103522661446FBA
:202FC0000552116106524060088F8A021F76BE01059AA82801FC0AA81A1E1F76BF01021AF2
:202FE0000800326F088F00001F76BE01049AA82801BC0AA81A1E286F088F49031F76BE015A
:2030000005020AA81A1E206FA928CB0BA82808091F76BE010A1E059AA828257C1A1E146F61
:20302000088F8A0B1F76BE01059AA82801F80AA81A1E0A6F2FFF10161F76BE010A1E059AD6
:20304000A82813BC1A1E1A7669FF0600BDB2BDAABDA202FE1F767D03019200D200D30252C5
:2030600032601F76BF0116D0008FC0DB0992C1FF94CDFEFF01F016D116D00190A8CAA9CD69
:20308000FDFF949616D0099203F01F766F030290A8CA9C9600CDFFF39492039089FFA8CAE7
:2030A00000961F76BF010040036C0102A9860042026C02DA004A026C04DA0044026C08DA8E
:2030C00010D0947A1F767D03019201523160408DC0DBA18A1F76BF01A183877620A116DCBA
:2030E000C418FDFF16DD0992C3FFA18A03F00290C59816DCC418FBFF0992412B008F4CDF12
:20310000C1FF01F00190A95DA606407646800356A902A18A1F766F0316D0079016DCC4983E
:2031200000CDFFE79192069089FFA8CA00961F767D03019204525B60408DC0DBA18A1F76B2
:20314000BF0116DCC418FEFF01CC0004C9FF412BA95D008F4CDF20FFE8034076468016D9AA
:203160001F76BF010190C198014B026C01DA0148026C02DA0945026C04DA0942026C08DAB3
:20318000014A026C10DA0147026C20DA0944026C40DA0044046CA9AA8009A9860149056C8D
:2031A000A9AA18FF0100A9860941056CA9AA19FF0100A986004B056CA9AA1AFF0100A986C7
:2031C0000045056CA9AA1BFF0100A9860940056CA9AA1CFF0100A986004A056CA9AA1DFF1A
:2031E0000100A98610D0008FC0DB947A1F767D030192065210601F76BF010046026D01DA32
:203200000048026D02DA004B026D04DA10D0008FC0DB947A1F767D0301920552E0FF890004
:203220001F76BF010945036D01DA026F10DB0944036D02DA026F40DB0042036D04DA056F1B
:20324000A9A21CFF0100A9820044036D08DA056FA9A21DFF0100A9820046036D10DA056F2B
:20326000A9A21EFF0100A9820040036D20DA056FA9A21FFF0100A9820048036D40DA056FC1
:20328000A9A21FFF0200A982004A056DA9AA8009A986056FA9A21FFF0400A9820043066D0F
:2032A000A9AA18FF0100A986056FA9A21FFF0800A9820045066DA9AA19FF0100A986056F9D
:2032C000A9A21FFF1000A9820047066DA9AA1AFF0100A986056FA9A21FFF2000A98200418B
:2032E000066DA9AA1BFF0100A986056FA9A21FFF4000A982004B066DA9AA1CFF0100A9861A
:20330000056FA9A21FFF8000A9820049066DA9AA1DFF0100A986056FA9A21FFF0001A982C1
:203320001F767D0310D0008FC0DB10A2947A008FC06FCC92013B1F76BF01C4FF01F00190BC
:2033400009D0A985A9860192C3FF01F0019080FFA9850156A2000192C5FF01F0019081FF61
:20336000A9850156A2009492C2FF01F0019082FFA9850156A20017D0008FC0DB82FE947A76
:20338000BE82BE86BE8B06001F76BF0100920093C5FFD7FF01F001F10190019190FFA9CB3D
:2033A0000092CAFF01F0019081FFA8CA060009D0008FC06F1F76BF0194CD0004D9FF09CC3A
:2033C000004090FFCDFFA9CBC492C2FF01F0019081FFA8CA06001F767803BDB20B93A988FF
:2033E0000153BDAAE0FFEC001F767D0301930653E0FFE6001F765C030D93E0FFE1001F7697
:203400007803099308CBE0FFD9007FD0008FC0DB179394271660AB46036C1B46086CAB478F
:20342000036D1B47046D00530C600F6F0C1B900103680C288F01182B1728E803A61A0001F7
:203440000C1B1F03E7FFB700A6920552056018930361A61A0002AC28200311300E0DA98A3B
:2034600034FFA71E608F0000A9A832FFA7070156A5002406C5962406CD97D57EDD3F407601
:203480005197E596407664971F767D03008FC0DBA4C5ED961A061F767D037BDFF596188833
:2034A0001CDC08D009D1FD7EC792A4C5008FC0DB959600D0C7929D96A9A80AD1A00DA71ED6
:2034C000A61EA092A17218DFC7929D96A092A6C50ED1A17201D8C7929D96A0920452ED688F
:2034E0001F76780312D01992C076A6DF13D1959614D0C7929D9615D1CF92959616D0D79234
:20350000A4C59D9618D11A921F767C0379DF959617D01892959619D0BF569D06BF569510CC
:20352000C792A4C51AD178DF9D96C792A4C51BD07ADF95961CD0C792A4C51ED11F76780376
:203540006EDC6FDF95961DD0C792A4C5008FBCDD95961FD0C7929D9620D11E92959621D06E
:203560001F929D9622D12092959600D021929D9623D122929D9624D1A092A172949301D8CC
:20358000A0929D970452F8681F7678031B3FE5921C96A9282003ED930E0A1D970E54026078
:2035A0000E2B0C1B200302670C0A010201562600066FBF561801036FBF560B03BE86BE8BC4
:2035C0000600BDAAAA280100A888A9800876303BBE5AA75D008F0010A9A8A50F2B60A693E5
:2035E0005461A75DA9A8A50F2560A693015322601F767D03019206521D601F765C030D9269
:2036000019601F7678030B92015214610C2B7FD00E2B172B162B008FC0DB0D2B102B9492B7
:203620001B96092B082B019ABF560B0140767897316FA75D20FF0110A50F0B601F767803EE
:203640000B9201520661A6930461A61BE8031B69A75D20FF0210A50F0460A61B20030F689D
:20366000A78020FF0310A70F0860A692015205661F767803167E0E6FAA2B0C6F1F7678035C
:203680000D7E086F0F7E066F1F767803BF560B03172BBD7A0076AA92BE8606001F7678036E
:2036A0000C9311880E92150A111C0100172B0C2B1397127E14960E2B06000876303BBE2F73
:2036C0001F7678030B9201521860010201562400100A0F9210540566102B019A40767897CF
:2036E0001F76780317920961FF9C17960660059A407678974076DB98BD570076060008765E
:20370000303BBE2F049A40767897BD57007606000052A88005611F7678031393046F1F76C0
:2037200078030C93005203611488026F0E88005203611227026F1127A7550366009A1B6FF5
:20374000A692A89EA794A9082003A91B20030668A908E0FCA91B2003FC67AC28007DAB3282
:20376000AC282800A930AB07A40D208F00000156A400C4920600A988A693A80800F00F539F
:203780003B660356A601C0763AC1A927AB0800E0A706AB0DA71EC7C52076A9284D55EFFF47
:2037A000A000029AEFFF9D00289AEFFF9A00A9282003EFFF96001F7678030F92EFFF910050
:2037C0001F7678032406EFFF8C001F7678032406A80EEFFF86001F767D030192EFFF8100BA
:2037E0001F767D0300927C6FA928E803796F1F7678031792756FA61B10105D68A61B8810F7
:203800001D671F767803169304600B9301531661A692A908F0EFA988AC28CDCCA636AC2888
:20382000150022560D93A92D2831A692AC95ABEAAB5C169240761599536FA61B00113B68D4
:20384000A61B7C1138671F7678030C9303530468AB280300026F0C27A60800EF2606AB011B
:20386000A71EA6920360A7923B6F01520460A706A80E366F02520360AB92326F03520560F7
:203880001F76780315922C6F84DEAC28CDCCA636AC2815002256A92DAB92AC540C690C93A3
:2038A000A692AB9F2831ABEAAC95AB5C009A40761599166F009A146F1F7678031692056192
:2038C0001F76780313920C6F0C920A6F1F76780316920361039A046F1F7678030B920600C2
:2038E0001F7678030B92015227601F767D030192065222600876303BBE2F7FD0008FC0DB70
:2039000094881F7678031B0EA60F116040765197A9881F7678031C0EA60F09604076649754
:20392000A9881F7678031D0EA60F0461039A40767897BD5700760600A9881F7678030B9231
:20394000015217601F767D030192065212600876303BBE2F1F767803197E8076A6DF029A97
:20396000C697CE7CD67D1A0A40767897BD5700760600608F00001F767803208F00FA28A064
:203980002AA828062C1E2E2B2806046FA98A842BA9A81F7678032A0FFA680600BDB200BED2
:2039A00000D0408FC0DBAA2B008F80DCA9A8A00DA98A9580C427A70EAB0F0361C47F01BE07
:2039C00001D8A0928052F168A6921061408D80DC809AA18A40760EB480D0A82802019196D8
:2039E000A18AA9287E3F40769BA1BE8B0600A888A927BDB2A9A8A48B1F767E03BDAA10FF52
:203A0000A101408FFFFFA71EA986A98AAB921B278050C73FCF960302D77E4076FDB3A9CD20
:203A2000FF00A7FFA283008D9E01FF90A18ADD97E5960502BF56910140760892BE86BE8B5C
:203A40000600BDB2BDAAA486008DA10100D5A9AAA50DA71EA9AAA50DA98A01DD0EDFC79236
:203A60009496A5920852F468A28A0EDC0356D408DCCAA9590356E408ECCAA993A11B001039
:203A80001667A19280520D677F520561008FC0DB9C97186FA89240769CA540760C99126F08
:203AA000069AA28A029B4076849A1E6FA19240766E9800520760069AA28A039B4076849A56
:203AC000136F008D9E01A11B0010BF569201036808BE026F0ABEA9AAA28A10FFA101A983FE
:203AE000A60640760892BE86BE8B0600BDB2BDAABDA204FEA4860EDC0356D408DCCA429639
:203B00000356E408ECCA4396412BFF9C7D52E7FFB600429280520567809A429E435426672F
:203B20004292A91B0010E8FFAA00A91B0F100666A9281010429E435419674292A91B10105D
:203B4000E8FF9D00A91B88100667A9288810429E43540C674292A98820FF0011A60FE0FFFC
:203B60008E0043927C52E0FF8A00A9AA10FFA101A71E1F767E031B92C79643920356A901EA
:203B8000BF56CF03D796421B001005680876303BBE924196439200D327614292A91B00100F
:203BA0000767A358A072008FC0DB9488066FA392429440764899A9880356A301A98003D102
:203BC000A9AA10FFA101A98AA793A17304D0A693D7FF9C97A793A073A6CCFF0001DB949686
:203BE0004392A354DB66421B001004684192BD96007643920356A9014196A993408FFFFF8B
:203C0000039AA894A959A9AA10FFA101A982A98AA10E4076FDB3A9CDFF0004D09B97419303
:203C2000A073A7FFFF9005D193964192A172069A4194A988079A9B2BA6594194A993419267
:203C40009B2BA85908D0A0729B2B932B008D9E01BF569201421B0010036805BE026F08BE89
:203C6000A9AA10FFA101A28AA98343920356A901A694A90E40760892066F039A029BA28AF4
:203C80004076849A84FEBE82BE86BE8B0600BDB210FEAD5C89DCA45DAD5CC07600C18FDCA4
:203CA000A45C08F6852400D0C07609C105F68424008FC0DB1F767D03019206520460A0922A
:203CC0007F520261942B01D8A0928052F4684076D2A4008FC0DBA4C51F767D0300921F76BD
:203CE0007D037EDFC79601920452086160D162D0BF569C0ABF569408116F1F76700300D011
:203D0000BF562002AD5C8FDCA45C9492008F22DC949601D8A0920652F6681F767D030692AD
:203D20001F767003BF5621651A611F767D0300880092095283DE026004BE00D00129008FCD
:203D400034DC0356A603C07601DDA00D0156A700C792949601D8A0920852F4681F767D0339
:203D600001920152116000D0008FC0DBA9A8A00DA98A01D830D1A092BF569C46105248D143
:203D8000BF569C3CF2681F767D0301920252216000D0008FC0DBA9A8A00DA98A01D830D15A
:203DA000A092BF569CAA065248D1BF569C96F26806D0008FC0DBA9A8A00DA98A01D830D159
:203DC000A092BF569C73085248D1BF569C69F2681F767D0301920352116000D0008FC0DB3E
:203DE000A9A8A00DA98A01D830D1A092BF569C46145248D1BF569C3CF2681F767D03009222
:203E000009521B6000D0013BAD5C89DCA480008F30DFA7069481A71E008F08DCC792949613
:203E200001D8A0920F52F1683DD1008FC0DB3ED0BF569C46BF5694461F767D030192065291
:203E40002560008FC0DB30D032D19428A401A4C531D09428A40133D09C2884037ADF942821
:203E6000840348D094282C0149D094282C014AD0942820034BD094282003C792A4C56ED1C4
:203E80006FD09C967BDFC79294961F767D0305920A6100D0008F30DC9428000801D8A09283
:203EA0000452FB684076D38390FEBE8B0600BDB2408D80DCA9287E3FA8280201A18A407686
:203EC000DEA1809AA18A40760EB4A98080D0A988910EA70F0E60A688012920FFFFFFA60F0F
:203EE00008611F767D0300887ED0910EA60F15614076D49B4076D2A440765B9A1F767D0393
:203F0000019201521F601F7670033F1A10001F765C030F28FFFF166F00D01F767D030192A5
:203F200006520460A0927F520761008F80DC9492008FC0DB949601D8A0928052EF6840760B
:203F4000D2A4BE8B06000600A983C57C0600BDB2BDAABDA206FE1F767803A482BF56080196
:203F60000EDCEC92FF90A95AE492FF90A988A9AA37FFA6AFA986DC92FF90A988A9AA37FF4D
:203F8000A6AFA986D492FF90A98809D0A9AA37FFA6AFA9869492FF9008D0A9599492FF904D
:203FA000A988A9B237FFA6AFA98BFC92FF90A988008D3203A9B237FFA6AFA98BF492FF9077
:203FC000A988408FFFFFA9B237FFA6AFA98BA9A210FFA101A71E441EA98A9392C7960202ED
:203FE000BF56CC344076FDB3A90E461EA28A465DA9B24076D3B3419644830102A38A407641
:20400000089244830102A38ABF56C534407608924192A90E461EC38AE446FE6CA9B2A38A5A
:20402000A28340769B93C38AE446FE6CA9A210FFA101A983A38A4692A993FF91A7FFFF9068
:20404000C597CD960602D52BDD2B4076089286FEBE82BE86BE8B060006000600A98AC49255
:204060000600A988BDB21F767103A9A8008D3203A48BBF560101BDAA10FFA101408FFFFF53
:20408000A71EA986A98A9193C7970202CF7E4076FDB3A9CDFF00A283A7FFFF90A18AD5975A
:2040A000DD960602E52BED2B40760892BE86BE8B0600BDB202FEA48B0EDCEC92FF90A980BC
:2040C000E492FF90A988A70637FFA6AFA71EDC92FF90A9880129A70637FFA6AFA71ED492F2
:2040E000FF90A988412BA70637FFA6AFA71EFC92FF90A9934196F492A838419620FF00808B
:20410000A70F0666A706415C4076319D086FAD5CA792029B81DCA45C40769BA1399AA18A0C
:204120004076BE9D82FEBE8B0600BDB2BDAA02FE1F767103A486BF5601010EDCEC92FF9088
:20414000A980E492FF90A988A70637FFA6AFA71EDC92FF90A988A70637FFA6AFA71ED49272
:20416000FF90A988A70637FFA6AFA71E012920FF0080A70F0666A7064076BB9D4196086F8E
:20418000AD5CA792029B81DCA45C4076DEA1A9AA008D320310FFA1011F767C03A71EA98BDB
:2041A000A98A9292408FFFFFC796BF56CF384192FF90D7964192A7FFFF90DF961292A9CDFC
:2041C000FF00A7FFFF90E197E99606024076FDB3A9CDFF0009D0A7FFA183A28AFF9008D19A
:2041E000F597FD960A029D2B952B4076089282FEBE86BE8B060002FEA61EAC281F00013BB6
:204200002256A60740FF0156A400C492A640036CFF90036FA7FFFF9082FE0600BDB2A48B3A
:20422000BDAAE1C50AD09183A706A58A02194076889EA927A706A58A01194076889EA1C5A8
:20424000008D3203408FFFFF0EDF0356A908AB94A95AA78A9192C7960202BF56CF33407614
:20426000FDB30AD0A95DE106918A02194076D3B3A988A20EA60F1061008D3C03912BD19263
:20428000025205601F76BF01081A0400A18A099A4076B390096FA18A339A4076BE9D008D15
:2042A0003C03BF569101BE86BE8B0600A4830EDDED92FF90A980E592FF90A988A70637FF22
:2042C000A6AFA71EDD92FF90A988A70637FFA6AFA71ED592FF90A98809D0A70637FFA6AF60
:2042E000A71E9592FF9008D0A90EE41E9592FF90A988E40637FFA6AFE41EFD92FF90A98805
:20430000E40637FFA6AFE41EF592FF90A988E40637FFA6AFE41E02020156E4000AD01F76BA
:2043200078033A9AF4C394C3BF5608014076BE9D06000600BDB2BDAABDA2A482008DA0015C
:2043400093923D61408DA0010FD09B2B935A008F40DE013BA9A8A281A98AC49203523268C5
:20436000A91B90012F66A29233521A61008F40DEA9A8A281A98AC458A059408FFFFFA38ABC
:2043800082D981D8A1880EDC035694089CCAA959A6064076FDB3A188A90EA60F05600CD011
:2043A000A292937A1C6FD392025205601F76BF01081A0400A38A099A4076B390A928FFFF00
:2043C0000E6FD392025205601F76BF01081A0400A38A099A4076B390A928FFFFBE82BE86AB
:2043E000BE8B0600BDB206FEA48B0EDCEC92FF90A90E421EE492FF90A988420637FFA6AFB5
:20440000421EDC92FF90A988420637FFA6AF421ED492FF90A98809D0420637FFA6AF421EA9
:204420009492FF9008D0A90E441E9492FF90A988440637FFA6AF441EFC92FF90A988440621
:2044400037FFA6AF441EF492FF90A9880DD0440637FFA6AF441E9492FF900CD0A90E461E69
:204460009492FF90A9880BD0460637FFA6AF461E9492FF90A9880AD0460637FFA6AF461E45
:204480009492FF90A9880ED0460637FFA6AF461E94CCFF0004520A61055212604206448A1E
:2044A00046C4A6934076DEA1076F4206448A46C4A69340769BA13C9AA18A4076BE9D86FE2D
:2044C000BE8B0600C4A0D41A01000600C4A0D41A02000600D493009A039100530260019AF5
:2044E00006001F76BF010118F7FF06001F76BF01011A08000600227608D0408F806F1F760B
:20450000BE0195CCFCFF015008960BDD08CCF7FF0450089608CCDFFF1050089608183FFFDF
:204520001F76BF01C51A08000118F7FF1A761F76C10169FF0002BF560007BF56011F022B61
:20454000BF56040C0A2800800B2B0C2BBF560F10D42BC41E001A800006001F767A032388A5
:20456000BDB2A48BA6921352E6FF5801C07612C10129A7060456A601A71EC7C52076C18AAF
:20458000240ED40F0561BF562311EFFF470108D091C5673E1F767A03232B242BD118F7FFC0
:2045A000D118FDFFEFFF3A011F76C1010246ECFF3501C18A1F767A03240EC407A71E1F7679
:2045C000C10107921F767A03C796BF562313240AEFFF24011F76C1010246ECFF1F01079242
:2045E0001F767A03BF5623112596EFFF17011F76C1010246ECFF120107921F767A03BF5642
:20460000230F2596EFFF0A011F76C1010246ECFF050108D091C5673E1F76C10107400B6D3B
:20462000D118FBFF1F767A03D118FEFF232B242BEFFFF4001F767A03BF562309EFFFEE00F1
:204640001F76C1010246ECFFE90007921F767A03BF56230B2596EFFFE1001F76C1010246D0
:20466000ECFFDC0007881F767A03257EC18A240ED40F0561BF562307EFFFD00008D091C53E
:20468000673E1F767A03BF562309EFFFC7001F76C1010246ECFFC20007921F767A03BF5661
:2046A00023072596EFFFBA001F76C1010246ECFFB50007921F767A03BF5623052596EFFF9D
:2046C000AD001F76C1010246ECFFA800079208D01F767A03259691C5673E1F767A03BF569B
:2046E0002303EFFF9B00D140086CBF5623010AD0D11A040091C5673ED141ECFF8F001F7668
:204700007A030AD0BF56230DD11A080091C5673EEFFF84001F76C101BF56008F082B021A53
:2047200020001F767A03BF562312776F1F76C101BF56008FC18AE4920896021A20001F76EC
:204740007A03BF562310696F1F76C101BF56008708280003021A20001F767A03BF56230E02
:204760005C6F1F76C101082B021A20001F767A03BF56230C526F0AD091C5673E1F76C10165
:20478000BF56008708280005021A20001F767A03BF56230A426F24801F76C101BF56008F68
:2047A000C18AA706C407A71EC79208961F767A03240A1F76C101021A20001F767A03BF5680
:2047C00023082B6F1F76C101BF56008FC18AE4920896021A20001F767A03BF5623061D6FA2
:2047E0000AD091C5673E1F76C101BF56008708280002021A20001F767A03BF5623040D6FB9
:204800001F76C101BF56008708280006021A20001F767A03BF562302BE8B06001F763000D3
:20482000A880A927A48E142820400002008FB2DE1F767C03121E4076F79F0052FD61A79218
:20484000A793C0FF0191A995A880A7922661408FA6DEAB9220BE1F90A674A792A6540267D4
:20486000A7881F767A03A65C0356AB01263AA90E28A82A1E008FB2DE4076EF9F4076F79F12
:204880000052FD61A93AA60DA98EAB92A694A927A792A69EA980DE601F763000142810401F
:2048A00006001F763000A880A927A48E142820400002008FB2DE1F767C03121E4076F79FB6
:2048C0000052FD61A792A793C0FF0191A995A880A7922661408FACDEAB9220BE1F90A67401
:2048E000A792A6540267A7881F767A03A65C0356AB012C3AA90E2EA8301E008FB2DE4076BE
:20490000F39F4076F79F0052FD61A93AA60DA98EAB92A694A927A792A69EA980DE601F767C
:20492000300014281040060002FE1F767A0336C5008FB2DE673E22761F763400008FB0B58F
:204940001CA81A7669FF4076BDB41F7671030F8F40423A0642A8C00020B6A9BD120F00778D
:20496000007702E84116008F10DF89E600004076FCB42376002082FE0600BDB2BDAABDA2B8
:2049800002FEA85BA58BA4864B9303524197C12B146106520F60A31B00103868A31B031048
:2049A0003566A39240766E9800523260BF56C1032F6FBF56C1012C6FA38820FF0010A60F8F
:2049C0000460419210521561A38820FF1010A60F0860419228520D6150520B617852096144
:2049E000A38820FF0011A60F116041927C520E60005200D11161A192A394407648999A9661
:204A000001D94192A154F866076FBF56C102C27B4193029ACA9782FEBE82BE86BE8B0600E7
:204A2000BDB2BDAA02FE1F767C03219225611F76800108421A6D0A420A6D1F767C03FABEDD
:204A40000A061F767C032E03A60F726904021F768001061E0A426C6C1F767C03212B1E2B64
:204A60001F7680010A1E0D6F1F767C030402212B1F768001081E1F767C031E921F5423661A
:204A80001F767C031A921C610876303BBE5A1B59AD5D1C93008F40DD1D88A1CCFF0082DD8F
:204AA0001A2BA55D417E40764AA21F767C03A1CD00FF1F2B42CB1E962097BD7A00761E92AE
:204AC0001F5436691F7680010406A940316D1F767C03008F40DD1F581F59012904B601D8B1
:204AE0009C2504BE94AF5AFF1F92C0FF1F768001A908001F08C3A9251F767C030AC51F7630
:204B00007C0320AF1F7680010AC21F767C031F0802001F768401161E1F767C03BF5621018F
:204B20002EC31F76840114A91F76800104C282FEBE86BE8B0600BDAA34FEA892FF9C0564E7
:204B4000105204640F9A026F009A22761F76BE01AB28F0FFAA28457F09CDFFDF013B08B6E5
:204B6000A81A0010A4A908BE099700D209CDFF3FA81A00400997A9851F7680015AFF2AC39E
:204B80005D56A400A0A9AB2BAA28467F08D42CC2ACA91F7684015AFF5D56AC0002A8A5A91F
:204BA000AB28E0FFAA28457F0AC3AB03AB28FAFFAA28FFFF12C21F76800107BE00AA1F76A8
:204BC0008401003A08A0101E1F76800102A900C200BE0002011960C2601A80F2081E0002AD
:204BE00060C401190C1E000201191E1E00020119221E00020119181E14C21844FF6C16068E
:204C00005E1E5E18FFFD5E1A00015D1800FF5D1A05005E1A7F005E18FAFF5EC416C2601865
:204C2000FFEF6006141E1844FF6D0002488F70641F768301541E088F7064541A010000A074
:204C40000031531A0200010200BE02A8541A06001F768001408F73B400D4301E54C502028A
:204C600032A82EA9008F48B41F76370008A01F768001241E26C220C31F7637000AA81F764E
:204C80003300321A1000321A2000237600011A761F767C0369FF000200B6182B261EB4FE82
:204CA00022C224C3BE860600BDB2BDAA08FE00021F767E030ED0081E9406481E4792A980E5
:204CC0000CD094C420FF001FA70F11600AD094920F9008524C60A6061F767C03A80E489345
:204CE0001B971C961D7EBF561A01416F4806A80EA95AA9CC008000D14396A292A9CC004046
:204D00004296A292A9CC00204196A218FF1FA606A80E489346974596447EAD5C83DCA45C24
:204D20009C921561A292805212677F520A61AD5C86DCA45CA2589C92008FC0DB9496076FB7
:204D4000AD5C86DCA45C9C9240769CA501D9A19201DA0352E3684806455C445DA80E46931C
:204D60004076299A1F76BF010F1A010088FEBE86BE8B06001F767C031992A88809611F7634
:204D800080010806A940046D0A06A9403E6C02D5A692013BAB28010001B6A572AA2B1F7630
:204DA0008001A9A8A581A98308A90AC301B6C58500915AFFA692A772A9A8A781A983C58526
:204DC0003FFFABCAAACBA71EA9A8A681A98A1F768401C48500915AFFA6853FFFAACBABCA9B
:204DE000A81A00E0061E04C322761F76800100022E1E1A760102041E1F76BF010E1A0100F7
:204E00001F767C03BF56190169FF0600009A019C1952FE640600005206601F76BF010D1AA3
:204E20000010056F1F76BF010B1A00100600BDB21F76BF01BDAA0D1A0008BDA2A95B01029E
:204E4000407613B700D1808DC06FA28AA392CEFF0BDCC41A0080407698A4A39280FFA95BA8
:204E60000102407613B70DDA0102C21A0080407613B701D9A1921052E7641F76BF010B1AB5
:204E80000008BE82BE86BE8B06000876303BBE931F767D030192065206617FD0008FC0DB1D
:204EA000942B066F008FC0DB7FDCC418C000BD9700760600CC920461CC0B0260D42B0600CC
:204EC000A980A8881F767D031E0EA70F1A61008FC06FA4831F76BF010DDDC51A00400D1A9D
:204EE00000041F767D031E92015204600BDCC41A00401E92025205601F76BF010B1A000446
:204F0000A6881F767D031F0EA60F24611F9212601F767D03019206520861045206611F7609
:204F2000BF010B1A0020166F1F76BF01021A0800116F1F767D0301920652086104520661C8
:204F40001F76BF010D1A0020056F1F76BF01041A08001F767D031D0E1A1E06000876303B5F
:204F6000BE271F767D0301921F767D031E881F9306520A6014920861008F5CDF4076E7A45B
:204F8000A6924076EDA4BD3F00760600C496CC2BD42BDC2B06000876303BBE931F767D036E
:204FA0000192065224607FD0008FC0DB9492008F5CDF407653A5008FC06FA4830DDDC51ABD
:204FC000004001921F76BF010D1A000406520761045205610BDCC41A0020036F021A080087
:204FE00000BE1F767D031AC2BF561401BD9700760600A988C4925EFFC47EA6CEA646096D0C
:20500000A9470D6CBF56D401CC97BF56DC01076FA946056CBF56D402CC97DC2B0600087639
:20502000303BBE271F767D037FD0008FC0DB1E2F1F801F767D039496019306530F601F7676
:205040007D0314930B61A828E803008F5CDF407686A5AA92A7934076EDA4BD3F0076060022
:205060001F767D03BF56120513287017008F59021F767803BF56203C1F767803BF5621A0DC
:205080001F7678031F2870171F767D0318A80600BDB2BDAABDA202FE79D0008FC0DBAC1EE5
:2050A000013BA98B94854C82A61E4456A90042A2C00020B678D0A98342A22156940044561B
:2050C000A100C00020B67FD0A607A5039449046C7AD0013B9485A918FF0FA95AA91A0080F4
:2050E0004076A4A47BD0A9B2408FC0DB34FF957AA61EA9B242A232FFA607C00020B6A98AB6
:2051000042A2C802AC1EA9B24456A900C00020B61CD0A49E82FE289C9596BE82BE86BE8B79
:205120000600A92802904076A4A41F767D03019206520F6104520D61025206611F76BF01C4
:205140000F1A00040A6F1F76BF010F1A0040056F1F76BF01061A0002060002FE1F767D03E5
:205160001292150A15546F661806152B4C607FD0008FC0DB9447176C1392C0FF1654026716
:20518000160A1392160A165402671696C4E21600C4E213014FE800C84FE80980007710E708
:2051A0004000086F1792162B90E5019CA918FF0317968EE6000016801388A9BF120F1793F4
:2051C0001F7678031E7F1F7678031F7E1F76780322960D617FD094480A6D1F767D031388ED
:2051E000013BA98542C24076D5A52D6F40761EA61F767D03008FC0DBBF5617017FDCC41858
:20520000FFFE216F18060119A61E181E20FF5802A60F136120FFF401180F0F6120FF9001D2
:20522000180F0B6120FF2C01180F0761C802180F04616402180F076040761EA61F767D0327
:20524000BF56170182FE060002FE22761F767D03019204520460008F96AE036F008F79A8AC
:205260001F7634001AA81A760F8F40421F76710369FF3A0642A8C00020B6A9BD120F0077C4
:20528000007702E8D123008F08DF89E600004076FCB41F7630000C2820402376001082FEEC
:2052A0000600BDB2BDAABDA240765EAE00D3103B69FF40764AB6267600002F7600004076BE
:2052C000F7B640762CB5407696941F767D03009A0093407628A30D9A40767E91418F00C2E9
:2052E00001D4A9A04076CD90418F00C202D4A9A04076CD90407621A24076FE93008FC06F3B
:20530000A48300D20BDC0CDDC51A0100C41A0100012920FF50C340761BB5008FC06FA4833E
:2053200001DAA2920EDC0FDDC51A0100C41A01000A52EF64008FC06FA4830BDC0ADDC51A88
:205340000100C41A01004076CC934076469A1F767D03109B009240760EB74076B1A6407632
:2053600069B61F767D030192045205614076E7AF40763784407663854076E49C4076D3830D
:205380001F767D030192065203604076BDA5407658A51F767D03C076C0DB00927EDFC796AD
:2053A00022761F76C001BF56292F1A7669FF40769DA21F765C0305926761052B00D2008FC1
:2053C000C0DB013BA9A8A281A98A60D09488A6925761008FA9DFA9A8A281A98AA692C40A4F
:2053E000C4544E66408FBEDEC42BA9A0A281A98AC49280520868008FBEDE013BA9A8A28175
:20540000A98AC42B013B086F109AA794A9CCF0FFC4968052F167008FBEDEA9A8A281A98A18
:20542000C4800356A203408FF0D8A61EA792C3FFA927A606AB0D0156A500A7CD0F00A82D4C
:20544000C59262FFA988E161013B0A6F008FBEDEA9A8A281A98AA692A0FFA988C40AA640DE
:20546000F66C408DBEDE013BA9B2A281A98B008FC0DB009AC193407647A4C108030001DA13
:20548000A29202529D647FD0008FC0DB9441066C7FDCC418FDFF4076D49B7FD0008FC0DBF2
:2054A0009442066C7FDCC418FBFF40765B9A7FD0008FC0DB9443066C7FDCC418F7FF407623
:2054C000E49C7FD0008FC0DB9445066C7FDCC418DFFF40765C804076B3951F767D0301923B
:2054E0000152136016D0408FC0DB9592A58AC1FF01F016D0A3CE0356A9027FDC0790C498E6
:2055000095CC0400C1FFA95B4076FD9900D2A2920461408D40D0036F408DC0D3A18A40761B
:20552000279FA91BFFFF53611F76BF0100BE0F1A01001F767E03375208C2116237523061FC
:20554000335208623352386103523E61065238613E6F34522D6135522761396F3A520862EB
:205560003A5212613852186139521261306F3B5207613C522C60A18A40767F9F286FA18ABC
:205580004076269F246FA18A4076E39E206FA18A4076E69D1C6FA18A4076229E186FA18ACA
:2055A0004076B99D146FA18A4076BA9D106FA18A4076349D0C6FA18A40769B9E086FA18ABC
:2055C0004076AE9A046FA18A4076039B01DAA2920252E3FFEDFE9C6F1B76F0FF0500BDABB3
:2055E000BDA8BDA0BDC2BDC300E2BD0003E2BD0003E2BD0103E2BD0203E2BD0330E60006A1
:2056000002FE69FF42291656227601021F767C0301560A00237600102676391110291A76E3
:205620007FD0008FC0DB944F046D69FF407639AD1F765C03040A049214520468042BBF56EB
:20564000050116D1008FC0DB7FD09C9294CA0190A9880C9301F10191A6CB10601F767D037D
:205660000193065306611F76BF010B1A0008146F1F76BF010B1A00200F6F1F767D03019310
:20568000065306611F76BF010F1A0008056F1F76BF010F1A00200653E1FF4B011F765C0339
:2056A000120A12930A53E0FFA1001F767D0301931F765C030553122B0360189322601F76F5
:2056C0007D030193015305601F765C03179319601F767D03019306530F6104530D6102535A
:2056E00006611F76BF010B1A0004226F1F76BF010B1A00401D6F1F76BF01021A0002186FF4
:205700001F767D03019306530F6104530D61025306611F76BF010D1A00040A6F1F76BF0148
:205720000D1A0040056F1F76BF01041A00021F765C0316930F601F767D030193035313619A
:2057400001531161065319601F76BF01021A8000186F1F767D03019303530F6101530D6108
:20576000065306611F76BF010B1A00100A6F1F76BF01041A8000056F1F76BF010D1A001073
:205780001F765C03179315601F767D03019306530B610353046101531561196F1F76BF0126
:2057A0000B1A0040226F1F76BF01021A80001D6F1F767D0301930653146103530D610153E7
:2057C00006611F76BF010B1A00800F6F1F76BF010D1A00800A6F1F76BF010D1A0040056F45
:2057E0001F76BF01041A020000523E601F765C03128062D0940EA70F37601F767D030193F4
:2058000003530F6101530D61065306611F76BF010D1A00100A6F1F76BF01021A8000056FD6
:205820001F76BF010B1A001006530E6103530961015304610D1A0080096F0B1A0080066F64
:205840000B1A0040036F021A800006530B6104530961025304610B1A0004066F0B1A004092
:20586000036F021A00021F765C03009A199300530260019AA693A9CF04611128F401132B8C
:20588000110AC4E21100197E12E8D01F14AD5764130A1393019100B6112B1497139307910A
:2058A000026101B6A692157F3C601F767D030092095224611F765C0316D0BF5616019493B2
:2058C0007FD0D0FF9492C9FFA8CAA940026C167F0C42116D0C430C6D0C44036D172B2F6F2A
:2058E0001592009B00520260019B1797286F14921796256FBF561701226F1F765C030C41EB
:20590000BF561801036C1492026F009A0C401796036D162B146F14931492189716960F6FE6
:2059200014931488149216971896177E086F69FF40763BA540763AA64076EA9882FEAFE23A
:20594000BE03AFE2BE02AFE2BE01AFE2BE0080E2BE00BEC5BEC4BE83BE8ABE870300303B35
:20596000F1FF177602761B76F0FF0500BDABBDAABDA8BDA0BDC2BDC300E2BD0003E2BD00DC
:2059800003E2BD0103E2BD0203E2BD0330E6000602FE69FF422916561F763300229241966D
:2059A00023760100267601012218FA002128FFFF10291F765C030E9202610E0B1F765C03F7
:2059C0000D92E0FF38011F765C030F920D61FF9C0F960A60008FC0DBA4837FDC7FDDC5187E
:2059E000EFFFC41A04001F767D030592196100D1008F0871013BA9A8A181A98AD492C3FFCE
:205A0000A988008FBCDDA9A8A181A98A69FFA192C47E4076018601D9A1920452EA641F7627
:205A20007D030192065237601F76C4010992C3FFA90EA9BD120F0077007763D0808DC0DBA6
:205A40008BE60000008F40D78CE6010069FF13E292014076E9B56ED16FD0013B008FC0DB94
:205A60009A92929EA985A9BD160F6FD0C8E29402007720E7800089E6090040764BB350E830
:205A800000C8007790E8008800778CE600001DD013E292001F767D0301920552406000D1FA
:205AA0001F765C0307921F7674033D0A3D5402623D2B008F0871013BA9A83D81A98A3D2DB9
:205AC000C492C3FFA90EA9BD120F69FF008F40D712350156A4008BE600004076E9B5013B1F
:205AE0001F767403008F86DE8EE60000A9A83D81A98A13E2C400008F30DFA9A83D81A98A58
:205B000001D9C492085205613D924076E482046F3D924076B780A1920652C3641F767D03B4
:205B20000492E1FF89001F76740301293E0640FFA61E013B3C85A60F39691F767D030492EA
:205B400000D13465008F0871013BA9A8A181A98A1F7674033D2D1F767D03C492C3FFA90E97
:205B6000A9BD120F0412A194A92D008F40D769FF8BE6000012350156A4004076E9B51F76D3
:205B80007403A192013B008F86DE3D2D1F767D030433AB72A9A8AB81A98A01D98CE600005D
:205BA000A19213E2C4000454CE641F7674033C0A3C92FF9CA9853E0F3E681F767D030492E8
:205BC00000D11F7674033C2B11651F7674033D2D1F767D0369FF0412A1944076B78001D906
:205BE0001F767D03A1920454F1641F765C0306921F767403029C3D0A3D5402623D2B1F7641
:205C00007D0302931F7674033D9200530F611F765C03065402600D9A1F7674033D931F7609
:205C20005C03069F015302600C9A69FF40762094026F0D0B008F0071A4831F76330019DCC5
:205C400001DDC51A0040C41A1000BF562101103B419282FE2296AFE2BE03AFE2BE02AFE298
:205C6000BE01AFE2BE0080E2BE00BEC5BEC4BE83BE8ABE86BE870300303BF1FF177602767C
:205C8000005206601F76BF01041A0001056F1F76BF01021A00010600005206601F76BF01DF
:205CA000041A4000056F1F76BF01021A400006004076CDAB019A4076D9AB009A4076CDAB90
:205CC0000102407613B7009A4076D9AB1E02407613B70600009A4076D9AB1E02407613B753
:205CE000019A4076CDAB1E02407613B7009A4076CDAB1E02407613B70600BDB2BDAABDA298
:205D00000EFE00D2A98BA288A492A52D0252A65BE3FFF000A9B2026301D256FFA983AC92C6
:205D200005640452036301D3026FAC2BAC92A58B4B2B0B650A02421EA9B2C00020B64B0A1C
:205D4000A98BAC924B54F762A9B20A650A02421EA9B2C00020B601DE58FFA98BF862A292BA
:205D6000A58BA694A988026001BEA692AC944B2B0452A9880F650A02421EA9B2C00020B621
:205D8000AC80A98B4B0AA69281DFFC9CA72D4B54F362AC920263AC2B019A66FFA958015288
:205DA000026000D08076E803A9B242C2C00020B6441EA9B242C2C0002FB6A61E6402421EEB
:205DC000A606C00020B6461E6402421EA9B2C0002FB6A61E0A02421EA606C00020B6481E7F
:205DE0000A02421EA9B2C0002FB64A1EA61E460648074407A607046000D0A02DAC5AA3923C
:205E00001561039A013B4B96AD888ADEA6880356A9010156A600C6060960AC924B5406610E
:205E20000F024B0BC61E4B92F062A2922061019A013B4B96AD5D8ADDA55D0356A9010156AE
:205E4000A5000F02C50F09614B9203520661019C04524B96F0640B6FAD5D8ADDA55D0ABED8
:205E60004B920356A9010156A500C5C2009A4B2B013BAD5D8ADDA55D0356A9010156A50001
:205E80008076B0DAA606C507A61EC685C51E019A4B2D66FFA0CE046180020156C5004B0ADA
:205EA0004B920452E76448A3AB92AA2BFF90A927A9A9AC28180037FF5AFF4A064E1E4E929F
:205EC000FF904E964D2B4E06ABCAAACB4E1E4606009BFF903FFF5AFF4E06ABCAAACB4E1E76
:205EE00044063B565AFF4E06ABCAAACB4E1E026F4EB2408F90DF013BA9A0A481A98A4B2BC2
:205F0000C45BA3921052246200D14E92013B0190A9884E0640FF4E1EA6924076E5AB01D93F
:205F2000A1920852F364A35A00D1A293A292B0FF0190A85A4076E5AB01D9A1920652F664FF
:205F40004076F7AB01DB4B0A4B920452DB648EFEBE82BE86BE8B060011D0008F10709492D1
:205F60000F9004520E60CCCC8001C6FF0252096022761F76C001BF562555BF5625AA1A7632
:205F800069FF060022761F76C001BF5629681A7669FF0600BDB202FE1F76C001A888A980E3
:205FA0001143026C257611CC80010561227611187FFE1A762276408F1070A58AA79211D0C2
:205FC0000F9001DCC41A400095CDF0FFA8CA21961A76A79204600102A98B026FA759A92807
:205FE00080C3A828C90169FFAC1E0556A10040764FADA1A91140FF6C22761118BFFF1A76CF
:20600000A6930153036102530A602276A69211CD7FFE039086FFA8CA11961A76A6930A6041
:20602000A9B2013BAC281E0040FF2256A10741FFA98BA69301530A60A9B2013BAC281E0084
:2060400040FF2256A10741FFA98BA69302530960A9B2AC281F00013B2256A10740FFA98B59
:20606000A7921460A69203521160227611CC7FFFA91A000169FF119620FFDA05407613B731
:206080001F76C001111A80011A761F76710382FE69FF3AB2BE8B0600BDB202FE22761F76A6
:2060A000C001BF561A011B2B1A92036001BE046F03561A01A9881F76710342C269FF3A060E
:2060C000C00020B63E1E1F76C0011B92036001BE046F03561B01A9881F76710342C23A0643
:2060E000C00020B6408D1070A18315D0008F200B3C1E0CDD1F762C0094CCF8FF015014DC5E
:206100003596C41A04003418F7FFC51A0800787680001F76C0011C1A1000A18A1C1A00043A
:206120001C1A00081C1A20000DDC10D91C1A00011C1A00101C1A00201C1A00401C1A0080FA
:206140001C18FBFFC41A01001D1A02001D1A04001D1A08001D1A10001D1A20001C1A0400A7
:206160001D1A00041D1A00081D1A00101D1A00201D1A00011D1A00021D1A00401D1A008068
:20618000C11A0001201A0002201A0004201A0008201A0010201A00201A7669FF82FEBE8B02
:2061A000060040764FAD049A029B407657AD4076D9AD060002FE22761F762B002028FFFF52
:2061C0002128FFFF2228FFFF2328FFFF2428FFFF2528FFFF2628FFFF2728FFFF1A761F7694
:2061E000FFCF69FF38924196399241963A9241963B9241963C9241963D9241963E924196ED
:206200003F921F762B0041962F9201F0019082FE06001B76F0FF0500BDABBDA8BDA0BDC21F
:20622000BDC300E2BD0003E2BD0003E2BD0103E2BD0203E2BD0330E6000669FF4229165656
:2062400000D1227601021F767C0301560A002376391110291A767FD0008FC0DB944F046DE4
:2062600069FF407639AD1F765C03040A049214520668042B1F765C03BF560501008FC06FB2
:20628000A4831F767D0306DDC51A0100019206520E6104520C61025205610FDCC41A00045B
:2062A0000A6F1F76BF010F1A0040056F1F76BF01061A00021F767D030192065208610452FD
:2062C00006611F76BF010F1A0020056F1F76BF01061A080001021F767E030807A61E008F4D
:2062E000204E081EA9A8A60F026708A8050A05920A520264052B0192AC2B1261008FC0DB4C
:20630000A9A8AC0DA98A62D0949205540465019A66FFA172AC92019C0154A92DF068040AA7
:20632000049202520265042B7FD0008FC0DB9493A89216D094CAA940EDFF8300A84B586DAF
:2063400020FF204E080F306904920A60013B1F766F0300D401D569FF008540760AAC1F7625
:206360007E03049201520A60008FC0DB013B01D569FFCC8501D440760AACA192408DC0DB78
:2063800002D400D569FFD1CEA90E40760AAC1F767E03049202527B60013B03D400D5D98507
:2063A00040760AAC746F0492076000D4000200D569FF40760AAC1F767E03049201520760AC
:2063C00001D4000200D569FF40760AAC02D4000200D569FF40760AAC1F767E0304920252BC
:2063E000566003D4000200D540760AAC506F0492086020FFB82200D401D569FF40760AAC99
:206400001F767E0304920152086020FFB82201D401D569FF40760AACA10E02D400D569FFDB
:2064200040760AAC1F767E03049202523060012903D400D520FFFFFF40760AAC286F69FF02
:20644000407692B604920760013B00D404D5008540760AAC1F767E03049201520760013BC5
:2064600001D404D5008540760AAC1F767E0302D400D5060640760AAC1F767E030492025244
:206480000660060603D400D540760AACAFE2BE03AFE2BE02AFE2BE01AFE2BE0080E2BE0010
:2064A000BEC5BEC4BE83BE8ABE870300303BF1FF1776027602FE2276008F40AA1F763500CB
:2064C0000AA81A7669FF4076E3B61F763300221A2000237601001F767D0301920652236082
:2064E000008F00711F76C401D4CCF0FF05500296DCCCF0FF0350039603CC1FFF105003965D
:2065000003CCFFF1A91A0005039603CCFF0FA91A00400396E4CCF0FF0750049604CC0FFF74
:20652000205004961F767D03019203521460008F00711F76C401D4CCF0FF02500296DC1819
:20654000F0FF03CC1FFF1050039603CCFFF0A91A000203961F767D03019201522860008F38
:2065600000711F76C401D4CCF0FF06500296DCCCF0FF0550039603CC0FFF4050039603CC79
:20658000FFF7A91A0007039603CCFF0FA91A00200396E4CCF0FF0350049604CC0FFF60502F
:2065A000049604CCFFF1A91A000104961F767D03019202520960408F0071A58A02DCC41895
:2065C000F0FFDD18F0FF019205524C60408F0071A58A1F76C40102DCC41A0F00DDCCF0FF26
:2065E0000250039603CC3FFF3050039603CCFFF0A91A0006039603CCFF0FA91A00C0039671
:20660000E5CCF8FF0A50049604CCBFFFB050049604CCFFF7A91A0007049604CCFF0FA91AEB
:2066200000400496EDCCF0FF0550059605CC1FFF1050059605CCFFFEA91A000E05960518A7
:20664000FF0FF5CCF8FF08500696061AF00006CCFFFDA91A000D069606CCFF0FA91A009003
:206660000696408F0071C0760068A5C41F76C401A78A1AD019DD01DEC61A000119DC011AFC
:206680000008C51A1000001A10000018BFFF001A000F011A00401F763300BF5621011F76EB
:2066A000A001C41A000819CCFFFCA91A0004199697CCFFFDA91A00011A96BF56098000CCC0
:2066C0007FFCA91A0001009600CCFFE3A91A0008009600CC001CC9FFA92D019A66FFA9881F
:2066E00000CC800304600102AC1E086F00CC8003013BC6FF80FFA985AC1E013BA6851F76E0
:2067000071034456A900421E3A06C00020B6008FA60E42A8C00020B61F767D0301931F768B
:20672000A00105960018FCFF025303602502026FBB021F76740382FE3E1E06001B76F0FF8F
:206740000500BDABBDA8BDA0BDC2BDC300E2BD0003E2BD0003E2BD0103E2BD0203E2BD03DE
:2067600030E6000602FE69FF422916561F7633003292419623760001267600013218750065
:206780002128FFFF1029408FC0D369FFA58A40768A9140768F91103B4192329682FEAFE2E2
:2067A000BE03AFE2BE02AFE2BE01AFE2BE0080E2BE00BEC5BEC4BE83BE8ABE870300303BC7
:2067C000F1FF177602761B76F0FF0500BDABBDA8BDA0BDC2BDC300E2BD0003E2BD0003E2F0
:2067E000BD0103E2BD0203E2BD0330E6000602FE69FF422916561F7633003292419623763B
:20680000000126760001321871002128FFFF1029408FC0D369FFA58A40768A91407660922D
:20682000103B4192329682FEAFE2BE03AFE2BE02AFE2BE01AFE2BE0080E2BE00BEC5BEC48B
:20684000BE83BE8ABE870300303BF1FF177602761B76F0FF0500BDABBDA8BDA0BDC2BDC359
:2068600000E2BD0003E2BD0003E2BD0103E2BD0203E2BD0330E6000602FE69FF4229165690
:206880001F7633003292419623760001267600013218FD002128FFFF1029408F40D069FF50
:2068A000A58A40768A9140768F91103B4192329682FEAFE2BE03AFE2BE02AFE2BE01AFE218
:2068C000BE0080E2BE00BEC5BEC4BE83BE8ABE870300303BF1FF177602761B76F0FF05001F
:2068E000BDABBDA8BDA0BDC2BDC300E2BD0003E2BD0003E2BD0103E2BD0203E2BD0330E62C
:20690000000602FE69FF422916561F7633003292419623760001267600013292322B21282E
:20692000FFFF1029408F40D069FFA58A40768A9140766092103B4192329682FEAFE2BE0379
:20694000AFE2BE02AFE2BE01AFE2BE0080E2BE00BEC5BEC4BE83BE8ABE870300303BF1FFF6
:20696000177602762276008F806FA483A4C41F76BE0126DDC51A030027DE261A0C00A483BC
:20698000261A3000261AC000261A000317DD261A000C261A0030261A00C0C61A0300271A70
:2069A0000C00271A3000271AC000271A0003271A000C271A0030271A00C0C51A0300A4834D
:2069C00028DD171A0C0016DC171A3000171AC000171A0003171A000C171A0030171A00C06E
:2069E000C51A0300281A0C00281A3000281AC000281A0003281A000C281A0030281A00C0EC
:206A0000C41A00C0161AC000161A0030161A000C1A7669FF06002276008F806FA4C4A483A9
:206A20001F76BE0118DEC61A030019DD181A0C00181A3000181AC000181A0003181A000C2E
:206A4000181A0030181A00C0C51A0300A48369FF191A0C0014DD15DC191A3000191AC000FA
:206A6000191A0003191A000C191A0030191A00C0C51A0300141A0C00141A3000141AC000E3
:206A8000141A0003141A000C141A0030141A00C0C41A0300151A0C00151A3000151AC000D5
:206AA000151A0003151A000C151A0030151A00C040763FB20600227615D0008F200B1F76A2
:206AC0002C0094CCF8FFA483A4C5A4C40150359614DDC518FCFF0CDF3418F7FFA4830DDE17
:206AE000341A0400C41A60000EDDC41A1C00C41A0300C41A0030C41A000EC41A8001CC1A01
:206B00004000C41A0040C41A0080211A0300C71A60002C1A1C002C1A03002C1A00302C1AD3
:206B2000000E2C1A8001C61A40002C1A00402C1A00802D1A0300C51A6000A4830FDD2E1A30
:206B40001C0018DC2E1A03002E1A00302E1A000E2E1A8001C51A40002E1A00402E1A0080D4
:206B60002F1A0300C41A0700381A38001A7669FF40763FB207F6007769FF0600BD3ABDB273
:206B8000BDAABDA202FE0129A9BF120F58FF5B61A85C7F91A8088000421EA493D6FFA85CB5
:206BA000A9BF160F6761A85D7F91A8088000A859A958A593D6FFA85DA493A571A818000119
:206BC000A697A418FF00A518FF007FDCA492A59EA7964D64A90801FF3E62A193A09236FF53
:206BE000A859A958420635FF0EF6A11FA95BA3010AF6A11F2D56A204A32DA03640FF0BF6DC
:206C0000A11F33FF009B30FF54FFA20CA39540FFA70801001FF677FF200940FFA7080100ED
:206C20001FF677FF5AFFA7922265A90801FF1363A9A946FF7F91A85BA95AA625A79596FF40
:206C4000A20CA395A9BD120F82FEBE82BE86BE8BBE8E0600009B57FFA8087FFF5AFFA69377
:206C6000F260A8280080AA71AB92ED6F20FF0000EA6F5AFFA493A818000196FFA85CA9A90A
:206C8000A8087FFFA81C0080A4CBDD6F58FF5AFF00BE25610129A6920190A958A60640FFF4
:206CA000A407A71EC793A640046DA892C7FFA92DA09201520460A8CCFF00A92DAC58A59270
:206CC000A0F201DEA092FF90A958A592408FC0DAC7FF9570A95DA9A9A60FDE66A592060088
:206CE000A71E8076C0DA0A6FA5928470FF90A95881DFA592C7FF9670A95DA706F660A59268
:206D000006000052A927FF76FFFF00BE3365A9A8013BA681A983A793C592FF90A92FAA718F
:206D2000A880009AA740046DB0FFA880056FB0FFA81C01A0A880019C0852F564A9A8013BD5
:206D4000A681A983A793C592C7FFA92FAA71A880009AA740046DB0FFA880056FB0FFA81CBE
:206D600001A0A880019C0852F56401DEAB92A654CF62A79206001B76F0FF00E2BD0030E63F
:206D8000000602FE422916561F7633003292419623760001267600013292322B2128FFFF14
:206DA000102901021F767C03180A1F768001061E1F763300211A0001103B419282FE3296BD
:206DC00080E2BE00303BF1FF177602761B76F0FF0500BDABBDA8BDA0BDC2BDC300E2BD00E6
:206DE00003E2BD0003E2BD0103E2BD0203E2BD0330E6000602FE69FF422916561F763300E2
:206E00003292419623760001267600013292322B2128FFFF1029008F006169FF4076E1A36D
:206E20001F76800102020C1E1F763300211A0001103B4192329682FEAFE2BE03AFE2BE0201
:206E4000AFE2BE01AFE2BE0080E2BE00BEC5BEC4BE83BE8ABE870300303BF1FF177602763D
:206E600000021F763000008F000C0119021E1F767C03408F100C000200A81F763000062BD7
:206E80000119072BE41A1000041A20001F763000008F080C0A1E1F76300000020119121EB9
:206EA0001F76300000020E2B0F2B1F767C0308A810A0021E1F763000162B172B1F763000F7
:206EC000E41A1000E51A10000C1A20001F763000141A20001F767C03121E0A1E060000E0EA
:206EE000E45003E2F40188E61000C48303E2D500C483F52BC483FD2BC483E51A1000C48392
:206F0000E51A2000C483E518FFFBC483E518FFF7C4830002E51A0040D41E06000129BDB2C1
:206F200040FFBDAAA98B00D208611002407613B701DAA9B2A20FFA66BE86BE8B06001F763B
:206F4000C001201A001040763FB22276008F200BA4C4A4831F762C000ED015DE14DDC6183D
:206F6000F8FFC51A03003418F7FF3418FBFF94CCBFFF20502E960FDC2ECCE3FF08502E967B
:206F80002ECCFCFF01502E962ECCFFDFA91A00102E962ECCFFF7A91A00062E962E187FFE38
:206FA000C418BFFF2E18FFBF2E18FF7F2F1A03001A7607F6007769FF0600AD28000469FF70
:206FC0001F5616561A5610E6000240291F76000002291B762276A92817B7A828000001099D
:206FE0001B61C07617B704290F6F009BA92401DF046C0429A82401DFA61EA1F78624A70622
:20700000A1810109A71EA92403635CFF043BA95901DF0900ECFF1A76A928FFFFA828FFFFAF
:2070200001090E61FF76FFFF066F01DFBDC3A71E673EBEC5A92401DFA82458FFF7604076C5
:20704000AFB64076CCB61B76F0FF0500BDABBDA8BDA0BDC2BDC300E2BD0003E2BD0003E2BF
:20706000BD0103E2BD0203E2BD0330E6000669FF42291656227601021F767C030156120096
:20708000008FB2DE69FF1F767A0338C5673E1A76AFE2BE03AFE2BE02AFE2BE01AFE2BE00E3
:2070A00080E2BE00BEC5BEC4BE83BE8ABE870300303BF1FF17760276BDB2CFE6010008D07D
:2070C00003E2BD040ED103E2BD0503E2BD0603E2BD07AFE2F400AFE2D40510E394040CD0DD
:2070E00012E39C1F0ED118E394DA0AE3C4A000E3E4C600E7BB0110E700010CD010E7C0008C
:2071000003E29C0203E2940008D0F406941E03E2F401AFE2BE07AFE2BE06AFE2BE05AFE285
:20712000BE04BE8B0600022904295F565AFF42065F56421E00021FF617564200AB0632567C
:2071400002292076022904295F565AFF420656FF421E00021FF617564200325602292076FC
:207160005AFF00021FF617564200A9A920765AFF00021FF6175642002076103B1F76330040
:207180002018FEFF222B242B69FF262B282B2A2B2C2B2E2B302B322B342B362B382B232BE4
:2071A000252B272B292B2B2B2D2B2F2B312B332B352B372B392B06001F763300201A0100E8
:2071C0002128FFFF102969FF0600A0E514AD0962A0E51F76740314AD90E5B4562901156F91
:2071E0004FE803C092E601008CB5050000E7CA00007700E78A000077CFE812F0007700E7AF
:207200005100007700E74000060002FE1F767E03020AC4E20200007712E8D01F14AD116419
:20722000030A0392022B0A520260032B033457040096060603610002036F00020119061E47
:2072400082FE0600A928FFFFAA28FFFFAB28FFFFA828FFFFAB0F04ED00D400BE0B6FA927E3
:20726000A928FFFFA92FA4A9C488A928FFFF0209A98AA6924076DEA606000077006F1F76CE
:207280007E03BDB220C5A959673E1F767E0324C5A70603ECA192673E1F767E03220603ECD2
:2072A000A71E673E4076CAB6BE8B060022761F76C00169FF1C1A0800787680001A761F76B8
:2072C000C401028FE64969FFBF5618E0A9A8407613B70600408F00C0008F000D2276009A80
:2072E00085C4019C805284C2FC641A7669FF06001F767E031EA806001F767E0320A806006C
:1473000006001F767E031A971B9606000119C356FFFF0600BE
:207314001201000000C0A78B0000A78B0000A78B0000A78B0000A78B0000A78B0000A78B28
:207334000000A78B0000A78B0000A78B0000A78B0000A78B0000A78B0000758C0000688C18
:207354000000F88F00009A8B00000F890000B18B0000828C0000458A00003A8A00002F8A3F
:207374000000248A0000198A00000E8A0000038A0000F8890000ED890000668A00005B8AB7
:207394000000508A0000DD8A0000C28A00000F890000B9890000A089000005900000718AB9
:2073B4000000D2890000078E0000D18D00009B8D0000658D00002F8D0000F98C00000F8978
:2073D40000000F890000228E0000EC8D0000B68D0000808D00004A8D0000148D00000F8978
:2073F40000000F890000CF8E0000B48E0000998E00007E8E0000638E0000488E00000F89B0
:2074140000000F890000DE8C0000C38C00000F8900000F8900000F8900000F8900000F890F
:2074340000000F890000A78A00008C8A0000F28B0000BC8B00000D8C0000D78B00000F8902
:2074540000000F890000DD8F0000C28F0000A78F00008C8F0000718F0000568F00000F89F5
:2074740000000F890000A88C00008D8C00000F8900000F890000498B0000F88A00000F89F5
:2074940000000F8900007F8B00002E8B0000648B0000138B00003B8F0000058F0000208FE3
:2074B4000000EA8E00000F8900000F8900000F8900000F8900000F8900000F8900000F8918
:2074D40000000F8900000F8900000F8900000F8900000F8900000F8900000F8900000F89D8
:2074F40000000F890000858900006A8900004F89000034890000198900000F890000288C5C
:207514000000438C00002C012C01000000000F00160014000A003C000A000800060008008F
:0A7534000600080033000B000A00F7
:20753E004800000012C100A100008DA10000EEA000007DA10000E1A000006FA10000D4A092
:20755E00000058A10000BAA0000048A10000ADA000003EA1000091A0000031A1000084A07E
:20757E00000023A1000077A0000017A1000061A000004CA000005A9900005E990000F399F7
:20759E000000E99900006199000064990000689900006D99000072990000789900007D991B
:1875BE00000081990000CD990000ED99000084990000F9990000000000
:00000001FF

View File

@@ -1,9 +1,9 @@
********************************************************************************
TMS320C2000 Hex Converter v22.6.0
TMS320C2000 Hex Converter v5.2.7
********************************************************************************
INPUT FILE NAME: <K:\git_project\Balsam_167_periph\Bin\UKSSTMS320F28335.out>
OUTPUT FORMAT: Intel
INPUT FILE NAME: <c:\Temp\166-sifu-build-vq3o6iha\UKSSTMS320F28335.out>
OUTPUT FORMAT: Binary
PHYSICAL MEMORY PARAMETERS
Default data width : 16
@@ -12,22 +12,22 @@ PHYSICAL MEMORY PARAMETERS
BOOT LOADER PARAMETERS
Table Type: SERIAL PORT (SCI 8 bit Mode)
Entry Point: 0x0000b581
Entry Point: 0x0000b56a
OUTPUT TRANSLATION MAP
--------------------------------------------------------------------------------
00000000..ffffffff Page=0 Memory Width=8 ROM Width=8
00000000..003fffff Page=0 Memory Width=8 ROM Width=8
--------------------------------------------------------------------------------
OUTPUT FILES: K:\git_project\Balsam_167_periph\UKSSTMS320F28335\Debug\.identity-release-cem4nfa7\.firmware-hb6330et\UKSSTMS320F28335.hex [b0..b7]
OUTPUT FILES: c:\Temp\166-sifu-build-vq3o6iha\UKSSTMS320F28335.bin [b0..b7]
CONTENTS: 00000000..00007627 BOOT TABLE
.cinit : dest=0000b72f size=0x267 width=00000002
.text : dest=00008000 size=0x372f width=00000002
.econst : dest=0000c000 size=0x11e width=00000002
.switch : dest=0000c11e size=0x48 width=00000002
CONTENTS: 00000000..000075d5 BOOT TABLE
.cinit : dest=0000b717 size=00000262 width=00000002
.text : dest=00008000 size=00003717 width=00000002
.econst : dest=0000c000 size=00000112 width=00000002
.switch : dest=0000c112 size=00000048 width=00000002
--------------------------------------------------------------------------------
00000000..ffffffff Page=1 Memory Width=8 ROM Width=8 "*DEFAULT PAGE 1*"
00000000..003fffff Page=1 Memory Width=8 ROM Width=8 "*DEFAULT PAGE 1*"
--------------------------------------------------------------------------------
NO CONTENTS

Binary file not shown.

View File

@@ -2,7 +2,7 @@
Текущая логика дискретов описана в [SIFU_START_STOP.md](SIFU_START_STOP.md):
CAN-фронты обрабатываются при приёме, импульсы снимаются таймером, STOP
имеет приоритет. Компактный формат записи v4 совпадает с исполнением 167.
имеет приоритет. Формат записи v2 и интерфейс SetGUI сохранены.
## Назначение и работа оператора
@@ -13,9 +13,8 @@ CAN-фронты обрабатываются при приёме, импуль
Общий RS-адрес 16 здесь не рекомендуется: ответ содержит собственный адрес,
и SetGUI проверяет совпадение адресов. CAN-настройки: [PERIPH28335_CAN.md](PERIPH28335_CAN.md).
Используется формат v4 из 167: 12 слов на запись вместо прежних 40;
ёмкость исполнения 166 сохранена — два банка по 800 записей.
Для чтения нужен SetGUI с поддержкой компактного формата v4, как для 167.
Перенесён протокол v2 из 167: 40 слов на запись, два банка по 800 записей.
Существующий SetGUI понимает эту прошивку без изменений программы.
Кнопки записи управляют регистратором; команду запуска УМП подаёт контроллер.
1. **Начать запись** включает только регистратор, не пуск УМП.
@@ -46,8 +45,7 @@ EEPROM не используется: под отключением в этой
## Память и точность
Использована существующая секция `.logg` по адресу 0x200000: 64000
16-битных слов. Два банка по 800 записей × 12 слов используют 19200 слов;
размер выделенной секции сохранён. Одна запись занимает 24 байта вместо 80.
16-битных слов. Два банка по 800 записей × 40 слов занимают её целиком.
Текущий банк кольцевой. При триггере сохраняются последние до 399 записей,
добавляется событие отключения, оставшаяся половина банка отведена
постинтервалу. При 20 мс это до примерно 8 секунд до отключения и 1 секунды
@@ -63,7 +61,7 @@ EEPROM не используется: под отключением в этой
одинаковую отметку; порядок сохраняется порядком записей. На перезапуске
регистратора счётчик продолжает прежнюю шкалу, при перезагрузке платы — 0.
Онлайн-опрос возвращает до десяти последних записей каждые 40 мс. Он не гарантирует
Онлайн-опрос возвращает три последних записи каждые 40 мс. Он не гарантирует
передачу каждого события при высоком CAN-трафике, медленном UART или паузах
GUI. Номера записей позволяют убрать дубликаты и явно показать пропуски.
Полный сохранившийся фрагмент следует читать кнопкой архива. Частота 20 мс
@@ -75,12 +73,7 @@ GUI. Номера записей позволяют убрать дублика
EEPROM, выделения памяти или копирования целого банка. Реальную задержку
прерываний и длительность критических секций нужно измерить на F28335.
## Состав записи v4: 12 слов
Состав и позиции полей совпадают с компактным логгером 167. Убраны сырые
данные АЦП и CAN, ошибки, счётчики, код и калибровки ЦАП, состояние разгона
и прочие дополнительные диагностические поля. Управление нагрузкой 166,
ёмкость банка и секундный постинтервал отключения сохранены.
## Состав записи: 40 слов
| Слова | Данные | Единицы/смысл |
|---|---|---|
@@ -88,21 +81,33 @@ EEPROM, выделения памяти или копирования целог
| 2 | Событие | 1 период, 2 CAN RX, 3 изменение I/O, 4 RS-команда, 5 конец постинтервала |
| 2, бит 8 | Триггер отключения | флаг поверх причины события |
| 2, бит 9 | Усечён постинтервал | установлен в конечном снимке |
| 3 | Commands & 0xC0 | только пуск УМП (бит 7) и сброс УМП (бит 6) |
| 3 | Commands / modbus[0x7F] | все 16 бит, в том числе пуск, сброс, инициализация и калибровка ЦАП |
| 4 | Входы OMEGA, Start_1, Res_in | биты 0,1,2, активный уровень приведён к 1 |
| 5 | START_DPTB, STOP_DPTB, RES_OUT_2 | биты 0,1,2, активный уровень приведён к 1 |
| 6 | modbus[0x1C] | расчётное задание в 0.1 мА; GUI/CSV переводит в мА |
| 7–8 | Фиксированные начальная и конечная уставки 166 | 6 и 16 мА; это не регистры настройки |
| 9 | ЗПТ1: U1 + U2 | ПМ67, регистр 306 / нулевой CAN-индекс 305, В |
| 10 | ЗПТ2: U3 + U4 | ПМ67, регистр 9 / нулевой CAN-индекс 8, В |
| 11 | Резерв | всегда ноль; чётная длина нужна для парной передачи CAN |
| 6 | IMPowse | остаток импульса, мс |
| 7 | WAKEpowse | остаток инициализации, тики DAC_FREQ |
| 8 | modbus[0x7B] | 12-битный код, переданный функцией dSEND в ЦАП |
| 9 | modbus[0x1C] | расчётное задание в 0.1 мА; GUI/CSV переводит в мА |
| 10–13 | sens_data[0..3] | измеренные сигналы, знаковое 16-битное значение в исходном масштабе |
| 14–17 | modbus[0..3] | маски ошибок измерений |
| 18 | CAN заголовок | адрес регистра в младших 13 битах и маска трёх слов в старших |
| 19–21 | Три слова последнего принятого CAN-кадра | без преобразования |
| 22 | Счётчик записанных CAN RX | младшие 16 бит |
| 23 | CanTimeOutErrorTR | младшие 16 бит счётчика тайм-аутов CAN TX |
| 24–25 | Фиксированные границы разгона 166 | 6 и 16 мА; это не регистры настройки |
| 26–28 | DAC_min(), DAC_max(), DAC_cal() | коды калибровки и ручного задания ЦАП |
| 29–30 | DAC_04, DAC_20 | значения соответствующих регистров |
| 31–32 | count_load, time_dac | положение и длительность разгона в тиках |
| 33–34 | Эффективные границы разгона 166 | 60 и 160 в 0.1 мА; GUI показывает 6 и 16 мА |
| 35 | kod | целочисленное положение, используемое Anal_output |
| 36–39 | adc_table_lem[0..3] | ADCRESULT2..5 после сдвига на 4, диапазон 0..4095 |
**Расчётное задание тока не является измеренным током выхода.** При включённом
**Отправленный код ЦАП не является измеренным током выхода.** В этом проекте
не установлено, какой вход аппаратно измеряет ток ЦАП. При включённом
`cCalibrDac` отправляется `DAC_cal()`, а расчётное задание 0x1C остаётся
результатом алгоритма разгона. Сырой код ЦАП и каналы АЦП в запись v4 не входят.
Напряжения обновляются по подтверждённым маской словам CAN даже при остановленной
записи: первый снимок содержит последние принятые значения. До первого приёма
они равны нулю; отдельного флага свежести в формате v4 нет.
результатом алгоритма разгона. Для диагностики это различие сохранено.
В 166 каналы «АЦП 0..3» берутся из `adc_table_lem`, обновляемой в режиме
нагрузки. Таблица `adc_table_tpl` в этом режиме не обновляется и не используется.
Управляющая формула 166 сохранена: `sinus = 10/16 * count_load + 2/16 * time_dac`.
Регулируемый разгон и ограничители 167 не переносились. Поля границ показывают
@@ -123,9 +128,9 @@ FC03, 0x1000, 16 слов:
| Смещение | Значение |
|---|---|
| 0,1 | 0x554D, версия 4 |
| 0,1 | 0x554D, версия 2 |
| 2,3 | состояние и число записей выбранного источника |
| 4,5 | 12 слов в записи, 800 записей в банке |
| 4,5 | 40 слов в записи, 800 записей в банке |
| 6 | период периодических снимков, мс |
| 7,8 | счётчик времени, младшее/старшее слово |
| 9,10 | Desk (требуется 6 / dsk_LOAD), Mode |
@@ -143,17 +148,15 @@ FC06:
- 0x1002: хронологический индекс первой записи окна 0..799;
- 0x1003: источник окна, 0 — текущий остановленный банк, 1 — архив отключения.
FC03, 0x1010, до 120 слов: окно до десяти записей от установленного индекса.
Для CAN длина должна быть кратна 12 словам: 12, 24, ..., 120; прежние
блоки v2 по 40 или 80 слов отклоняются как обрывающие компактную запись.
FC03, 0x1010, до 120 слов: окно до трёх записей от установленного индекса.
Чтение текущего банка этим окном во время записи не выдаёт изменяющиеся
данные. GUI сначала останавливает его; архив можно читать при работающем
текущем банке. После всех блоков GUI повторно проверяет поколение архива;
при замене во время чтения результат не подменяет предыдущий график.
FC03, 0x1100, ровно 124 слова: онлайн-блок. Слова 0,1 — 32-битный номер
первой записи, 2 — число записей 0..10, 3 — поколение архива, далее до десяти
записей по 12 слов. Неиспользованные слова равны нулю. Окно онлайн не
первой записи, 2 — число записей 0..3, 3 — поколение архива, далее до трёх
записей по 40 слов. Неиспользованные слова равны нулю. Окно онлайн не
зависит от курсора/выбора архива. Разделение 0x1010 и 0x1100 предотвращает
пересечение двух окон.
@@ -164,19 +167,7 @@ CRC и разбор стандартного ответа GUI использую
## Проверка и запуск
Для компактного формата v4 пройден host-тест настоящего `log_to_mem.c`
с MSVC 14.30.30705. Модули `log_to_mem.c`, `ecan.c` и `message.c`
скомпилированы TI C2000 22.6.0.LTS с COFF, FPU32 и O1 без ошибок.
После выбора исполнения 166 и периода CAN пульта 200 мс выполнена независимая
компиляция всех 27 C/ASM-модулей и линковка TI C2000 22.6.0.LTS с COFF,
FPU32 и O1. Загрузочный SCI8-образ занимает 30036 байт и побайтно совпадает
с образом, полученным из сохранённого `Bin/UKSSTMS320F28335.out`.
Проверено соответствие сохранённого HEX этому OUT. Сохранились пять
предупреждений в существующих `measure.c` и `peripher.c`; на плате не проверено.
Первоначально регистратор и RS/CAN-доступ перенесены из коммитов 167
`df71922` и `a3633d8`. Приведённые ниже результаты полной сборки относятся
к исходному формату v2 до сокращения полей.
Перенесены регистратор и RS/CAN-доступ из коммитов 167 `df71922` и `a3633d8`.
Сборка всех 27 C/ASM-файлов и линковка TI C2000 22.6.1.LTS (Debug, COFF,
FPU32, O1) прошли. Остались шесть прежних предупреждений в measure.c/peripher.c.
В MAP: RAML0 занято 0x38D1 из 0x4000 слов, .ebss — 0xFA9 из 0x3000,
@@ -185,10 +176,8 @@ FPU32, O1) прошли. Остались шесть прежних предуп
Сборка проверки лежит вне репозитория, штатные файлы Bin не заменены.
Host-тест компилирует настоящий log_to_mem.c с подменой GPIO/IRQ.
Проверяет кольцо, архив, переполнение постинтервала, онлайн-окно из десяти
12-словных записей, CAN-протокол v4, готовность именно 166, фиксированные
уставки 6/16 мА, маску команд и получение ЗПТ из трёх позиций CAN-кадра.
Сторожевые значения контролируют границу записи и разделение банков.
Проверяет кольцо, архив, переполнение постинтервала, онлайн-окно, CAN-протокол,
готовность именно 166, фиксированные границы и четыре канала АЦП.
Запуск из корня 166: `py -3 tests/ump_logger/run_test.py` (Clang/lld или MSVC
в PATH); при необходимости передать `--compiler "полный путь к cl.exe"`.
Тест не проверяет физические GPIO, тайминги прерываний и 16-битную ABI TI.
@@ -197,7 +186,7 @@ Host-тест компилирует настоящий log_to_mem.c с подм
начать запись → подать CAN-пуск → подать отключение → через секунду прочитать
архив. Сверить START/STOP с физическими выходами и измерениями, повторить с
разрывом RS/CAN. Проверить период 1 мс и задержки IRQ под нагрузкой.
Для первоначального формата v2 тесты SetGUI (`test_ump_logger.py`, `test_ump_logger_can.py`,
Тесты существующего SetGUI (`test_ump_logger.py`, `test_ump_logger_can.py`,
`test_periph28335_tab.py`) в окружении Qt 5: **47 passed**. Проверены протокол,
онлайн/архив, CAN-фрагменты, графики, курсоры и CSV.
Проверка на устройстве не выполнялась.

View File

@@ -1,123 +0,0 @@
#ifndef CAN_TELEMETRY_IMPL_H /* Реализация включается только в ecan.c после аппаратных типов и переменных штатного передатчика. */
#define CAN_TELEMETRY_IMPL_H /* Не допускает повторного определения состояния и функций в одной единице трансляции. */
#define CAN_HEARTBEAT_ADDR 0x0100U /* Как в варианте 167, отдельный адрес heartbeat находится вне R0–R127 и служебной области логгера 0x1000. */
#define CAN_HEARTBEAT_TICKS ((Uint32)READY_FREQ) /* READY_FREQ задаёт частоту Timer1 в герцах; это ровно одна секунда его тактов. */
#define CAN_REFRESH_TICKS ((Uint32)READY_FREQ * 13UL / 10UL) /* Тринадцать десятых секунды дают период повторения данных 1,3 с независимо от числа циклов main. */
#define CAN_SCAN_TICKS ((Uint32)READY_FREQ * CANPOWSE / 1000UL) /* Существующие CANPOWSE=20 мс ограничивают задержку обнаружения обновлений, но не скорость выдачи уже готовой очереди. */
#define CAN_ACK_TICKS ((Uint32)READY_FREQ / 10UL) /* Программная страховка через 100 мс отменяет зависший TX, если аппаратный timeout IRQ не был обработан. */
static Uint32 can_last_hi[ANSWER_LEN], can_last_lo[ANSWER_LEN], can_last_at[ANSWER_LEN]; /* Последний подтверждённый пакет и время ACK хранятся отдельно для каждого начального адреса R0–R127. */
static unsigned int can_valid[8], can_cursor[2], can_stream; /* Восемь 16-битных масок покрывают 128 адресов; два курсора и очередность обеспечивают справедливость потоков. */
static Uint32 can_tx_at, can_failed_at, can_heartbeat_at, can_scan_at; /* Беззнаковые отметки Timer1 поддерживают вычитание через переполнение 32-битного счётчика. */
static unsigned int can_backoff, can_started, can_heartbeat_count, can_heartbeat_due, can_scan_due; /* Отделяет секундную паузу после ошибки, старт часов, heartbeat и незавершённое сканирование масок. */
static int can_pending_addr; /* Значение -1 обозначает heartbeat, который не должен индексировать кэш технологических регистров. */
static volatile unsigned int can_tx_failed; /* Timeout ISR отмечает ошибку текущего mailbox до обработки ACK в основном цикле. */
static Uint32 CAN_millis(void) /* Возвращает согласованный счётчик Timer1; при READY_FREQ=1000 один такт соответствует миллисекунде. */
{ /* Чтение 32 бит защищается отдельно на 16-битном C28x без ожидания аппаратуры. */
unsigned int saved = __disable_interrupts(); /* Сохраняет исходное состояние IRQ, включая вызовы из уже защищённой отправки. */
Uint32 now = CpuTimer1.InterruptCount; /* Счётчик обновляют оба существующих обработчика Timer1 для пульта и измерительной платы. */
__restore_interrupts(saved); /* Немедленно возвращает прежнюю маску прерываний. */
return now; /* Наружу передаётся один целостный снимок времени. */
} /* Длительные расчёты масок выполняются с разрешёнными прерываниями. */
static void CAN_telemetry_init(void) /* Сбрасывает программное состояние при каждом InitCan, в том числе после запуска с грязной RAM. */
{ /* Крупные массивы не очищаются, потому что их элементы недоступны без valid-бита. */
unsigned int i; /* Перебирает восемь масок достоверности кэша. */
for (i = 0U; i < 8U; ++i) can_valid[i] = 0U; /* Первый пакет каждого разрешённого адреса обязательно передаётся после запуска. */
can_cursor[0] = can_cursor[1] = can_stream = 0U; /* Обе маски начинают обход с R0; далее потоки чередуются после реально поставленного кадра. */
can_backoff = can_started = can_heartbeat_count = can_heartbeat_due = 0U; /* Новые часы и секундный heartbeat начинают отсчёт при первом обслуживании main. */
can_scan_due = 1U; /* Первый снимок данных выдаётся сразу, без ожидания периода 1,3 с. */
can_pending_addr = -1; can_tx_failed = 0U; wait = 0; /* Исключает подтверждение кадра и ошибку из предыдущего запуска программы. */
} /* Сброс не меняет аппаратные CAN ID, маски регистров и настройки EEPROM. */
static int CAN_tx_ready(Uint32 now) /* Обслуживает завершение штатного mailbox 0; вызывающий код уже запретил IRQ на короткое время. */
{ /* Возвращает готовность без ожидания ACK, CANAA или освобождения шины в цикле. */
if ((ECanaRegs.CANTRS.all | ECanaRegs.CANTRR.all) & 1UL) { /* Бит 0 относится только к штатному TX; служебный mailbox 2 не перезаписывается. */
if (wait && !can_tx_failed && (ECanaRegs.CANTRS.all & 1UL) && now - can_tx_at >= CAN_ACK_TICKS) { /* Программный timeout страхует потерянное аппаратное прерывание спустя 100 мс. */
can_tx_failed = 1U; ++CanTimeOutErrorTR; /* Отмечает ошибку ровно один раз, пока этот запрос ещё ожидает завершения отмены. */
EALLOW; ECanaRegs.CANTOC.all &= ~1UL; EDIS; /* Снимает источник аппаратного timeout только для mailbox 0, сохраняя остальные биты контроля. */
ECanaRegs.CANTRR.all = 1UL; /* Запрашивает аппаратную отмену без блокирующего ожидания CANAA. */
} /* Данные занятого mailbox остаются неизменными до аппаратного освобождения. */
return 0; /* Текущий адрес и признак готовности сканирования сохраняются для повторного обслуживания. */
} /* Только свободный mailbox позволяет обработать завершение предыдущей передачи. */
if (wait) { /* ACK или ошибка относятся ровно к одному ранее запущенному кадру. */
if ((ECanaRegs.CANTA.all & 1UL) && !can_tx_failed) { /* Только успешный ACK позволяет подавлять повтор неизменившихся данных. */
if (can_pending_addr >= 0) { /* Heartbeat не занимает ограниченный технологический кэш. */
can_last_hi[can_pending_addr] = ECanaMboxes.MBOX0.MDH.all; /* Запоминает действительно доставленные адрес и первое слово из неизменявшегося mailbox. */
can_last_lo[can_pending_addr] = ECanaMboxes.MBOX0.MDL.all; /* Сохраняет второе и третье слова того же подтверждённого кадра. */
can_last_at[can_pending_addr] = now; /* Отсчитывает повтор через 1,3 с от последнего подтверждения, а не от неудачной попытки. */
can_valid[can_pending_addr >> 4] |= 1U << (can_pending_addr & 15); /* Устанавливает один бит достоверности начального адреса пакета. */
} /* Перекрывающиеся пакеты разных масок имеют независимые начальные адреса. */
} else { /* Отмена, ошибка или свободный TX без CANAA не подтверждают доставку текущих данных. */
can_failed_at = now; can_backoff = 1U; /* Ограничивает новые попытки одной секундой при отсутствии получателя и CAN ACK. */
} /* Старый подтверждённый кэш сохраняется, поэтому неудачная передача не теряет обновление. */
wait = 0; can_tx_failed = 0U; can_pending_addr = -1; /* Завершение обрабатывается один раз даже без отдельного CANAA после отмены. */
} /* Проверяет паузу после ошибки до подготовки следующего кадра. */
if (can_backoff && now - can_failed_at < CAN_HEARTBEAT_TICKS) return 0; /* Во время секундной паузы данные и heartbeat остаются ожидающими. */
can_backoff = 0U; /* Снятие паузы не требует изменения таймеров технологических данных. */
return 1; /* Mailbox свободен и может принять один новый актуальный кадр. */
} /* Чужие mailbox, аппаратный CAN ID и получаемые команды не меняются. */
int CAN_send(int Port, int data[], int Addr) /* Возвращает 0 при занятости, 1 после запуска TX и 2 при неизменном пакете до срока повторения. */
{ /* Снимок данных и запуск mailbox защищаются одним коротким запретом IRQ. */
Uint32 hiword, loword, now; /* Ровно два 32-битных поля соответствуют восьми октетам существующего формата BALZAM. */
unsigned int saved = __disable_interrupts(); /* Исключает разрыв трёхсловного снимка и гонку запуска с timeout ISR. */
(void)Port; now = CAN_millis(); /* Как прежний передатчик, использует eCAN-A; время берётся из общего Timer1. */
if (!CAN_tx_ready(now)) { __restore_interrupts(saved); return 0; } /* При занятости ничего не перезаписывает и не подтверждает данные программно. */
if (Addr == CAN_HEARTBEAT_ADDR) { /* Отдельный пакет состояния узла не обращается к data=NULL или к R256. */
hiword = 0xE0000000UL | ((Uint32)CAN_HEARTBEAT_ADDR << 16) | can_heartbeat_count; /* Сохраняет маркер BALZAM и передаёт циклический 16-битный счётчик секунд. */
loword = ((Uint32)(unsigned int)Mode << 16) | (unsigned int)Desk; /* Два следующих слова сообщают аппаратный Mode и тип платы Desk. */
can_pending_addr = -1; /* Успешный heartbeat не подтверждает ни один технологический пакет. */
} else { /* Первый снимок, изменение любого слова либо истечение периода вызывают одинаковую отправку данных. */
if (Addr < 0 || Addr >= ANSWER_LEN) { __restore_interrupts(saved); return 2; } /* Не допускает чтения за картой R0–R127 и позволяет планировщику пропустить неверный адрес. */
hiword = 0xE0000000UL | ((Uint32)Addr << 16) | ((Uint32)data[Addr] & 0xFFFFUL); /* Первый регистр занимает младшие шестнадцать бит старшей половины CAN-данных. */
loword = Addr + 1 < ANSWER_LEN ? ((Uint32)data[Addr + 1] & 0xFFFFUL) << 16 : 0UL; /* Отсутствующее второе слово у конца карты заменяется нулём. */
if (Addr + 2 < ANSWER_LEN) loword |= (Uint32)data[Addr + 2] & 0xFFFFUL; /* Третье слово дополняется только в пределах реального массива регистров. */
if ((can_valid[Addr >> 4] & (1U << (Addr & 15))) && can_last_hi[Addr] == hiword && can_last_lo[Addr] == loword && now - can_last_at[Addr] < CAN_REFRESH_TICKS) { /* Совпадение всех слов подавляет передачу только до индивидуального срока 1,3 с. */
__restore_interrupts(saved); return 2; /* Не сдвигает время подтверждения: неизменные данные всё равно повторятся по таймеру. */
} /* Изменение данных не ждёт окончания периода повторения. */
can_pending_addr = Addr; /* Подготовленное состояние попадёт в кэш только после успешного аппаратного ACK. */
} /* Для heartbeat и данных используется один и тот же безопасный жизненный цикл TX. */
ECanaRegs.CANTA.all = 1UL; ECanaRegs.CANAA.all = 1UL; /* Очищает старые ACK и отмену mailbox 0 записью единиц в соответствующие биты eCAN. */
ECanaMboxes.MBOX0.MDH.all = hiword; ECanaMboxes.MBOX0.MDL.all = loword; /* Записывает замороженный трёхсловный снимок без последующей подмены во время ожидания ACK. */
EALLOW; ECanaRegs.CANTOC.all &= ~1UL; ECanaRegs.CANTSC = 0UL; ECanaRegs.CANTOC.all |= 1UL; EDIS; /* Перезапускает аппаратный timeout только для нового штатного запроса TX. */
can_tx_at = now; can_tx_failed = 0U; wait = 1; /* Сохраняет момент запуска и принадлежность будущего ACK до разрешения прерываний. */
ECanaRegs.CANTRS.all = 1UL; /* Однократно запускает аппаратную передачу в штатном mailbox 0. */
led1_toggle(); __restore_interrupts(saved); /* Сохраняет индикацию передачи и возвращает исходную маску IRQ без ожидания шины. */
return 1; /* Подтверждает только постановку кадра, оставляя окончательную доставку аппаратному ACK. */
} /* Формат CAN ID, порядок слов и служебный mailbox логгера остаются прежними. */
void CAN_heartbeat_service(void) /* Обслуживает независимый секундный heartbeat и завершение ранее запущенного TX. */
{ /* Вызывается из main и не зависит от Cancount, масок данных или признака CanGO. */
Uint32 now = CAN_millis(), elapsed, seconds; /* Время Timer1 учитывает задержки main без накопления дрейфа секундной сетки. */
unsigned int saved = __disable_interrupts(); /* Короткая обработка ACK синхронизируется с timeout ISR. */
CAN_tx_ready(now); __restore_interrupts(saved); /* Сохраняет успешное завершение данных даже тогда, когда новых пакетов пока нет. */
if (!can_started) { can_started = 1U; can_heartbeat_at = can_scan_at = now; } /* Первый heartbeat появляется спустя полную секунду от начала обслуживания текущего запуска. */
elapsed = now - can_heartbeat_at; /* Беззнаковое вычитание корректно при переполнении 32-битного счётчика. */
if (elapsed >= CAN_HEARTBEAT_TICKS) { /* Формирует новую отметку на каждой секундной границе Timer1. */
seconds = elapsed / CAN_HEARTBEAT_TICKS; can_heartbeat_at += seconds * CAN_HEARTBEAT_TICKS; /* После паузы main оставляет последнее значение без очереди устаревших кадров. */
can_heartbeat_count = (unsigned int)((can_heartbeat_count + seconds) & 0xFFFFUL); can_heartbeat_due = 1U; /* Счётчик heartbeat циклически проходит диапазон 0..65535 секунд. */
} /* Занятый передатчик не теряет ожидающий heartbeat. */
if (can_heartbeat_due && CAN_send(0, 0, CAN_HEARTBEAT_ADDR) == 1) can_heartbeat_due = 0U; /* Приоритетно ставит один актуальный heartbeat после освобождения mailbox. */
} /* Следующий повтор heartbeat планируется секундной сеткой, а не длительностью обхода масок. */
void CAN_telemetry_service(void) /* Передаёт изменившиеся данные и повторяет каждый подтверждённый пакет через 1,3 с. */
{ /* Фоновое обслуживание не ждёт CAN ACK и выдаёт готовую очередь по мере освобождения mailbox. */
Uint32 now; unsigned int attempt, stream, pass, address, cursor; int result; /* Локальные счётчики ограничивают обход двумя потоками и конечной картой из 128 регистров. */
CAN_heartbeat_service(); now = CAN_millis(); /* Секундный heartbeat получает приоритет перед обычными данными независимо от настройки потоков. */
if (now - can_scan_at >= CAN_SCAN_TICKS) { can_scan_at += (now - can_scan_at) / CAN_SCAN_TICKS * CAN_SCAN_TICKS; can_scan_due = 1U; } /* Сохраняет сетку обнаружения изменений с интервалом 20 мс без накопления дрейфа после задержек main. */
if (!can_scan_due) return; /* При неизменных данных main не повторяет полный обход масок на каждой итерации. */
for (attempt = 0U; attempt < 2U; ++attempt) { /* После успешной постановки чередует потоки, исключая голодание медленной маски. */
stream = (can_stream + attempt) & 1U; cursor = can_cursor[stream]; /* Начинает следующий поиск после последнего принятого кадра данного потока. */
if (!Cancount[stream]) continue; /* Ноль сохраняет штатное отключение соответствующего потока; ненулевой период заменяется общим повтором 1,3 с. */
for (pass = 0U; pass < 2U; ++pass) { /* Два конечных прохода реализуют циклический обход без зависания на пустой маске. */
for (address = 0U; address < ANSWER_LEN;) { /* Формирует те же трёхсловные группы от первого разрешённого адреса маски. */
if (!(Maska[stream][address >> 4] & (1U << (address & 15)))) { ++address; continue; } /* Пропускает запрещённые регистры и гарантированно продвигает поиск. */
if ((pass == 0U && address >= cursor) || (pass == 1U && address < cursor)) { /* Сначала рассматривает хвост карты после курсора, затем оставшееся начало. */
result = CAN_send(0, modbus, address); /* Сравнивает полный снимок с подтверждённым кэшем и индивидуальным временем повтора. */
if (result != 2) { /* Только неизменный пакет до срока допускает переход к следующему кандидату в этом же вызове. */
if (result == 1) { can_cursor[stream] = address + 3U < ANSWER_LEN ? address + 3U : 0U; can_stream = stream ^ 1U; } /* Продвигает курсор лишь после постановки кадра и отдаёт следующий свободный слот другому потоку. */
return; /* При занятости сохраняет адрес; при постановке продолжит готовую очередь следующей итерацией main после ACK. */
} /* Пропущенные неизменные пакеты не занимают физическую шину. */
} /* Завершает проверку одного допустимого начального адреса. */
address += 3U; /* Как прежний протокол, один кадр покрывает три последовательных регистра независимо от разреженности маски. */
} /* Максимальный адрес ограничен размером реальной технологической карты. */
} /* После обхода обеих частей все пакеты потока рассмотрены ровно один раз. */
} /* Ни один разрешённый пакет не требует немедленной передачи. */
can_scan_due = 0U; /* Следующее плановое сканирование произойдёт через 20 мс; выдача готовой очереди этим ожиданием не задерживается. */
} /* Период 1,3 с относится к каждому пакету, а не к одному кадру всей маски за один период. */
#endif /* Закрывает единственную реализацию фоновой телеметрии текущей прошивки 166. */

View File

@@ -1,11 +1,9 @@
/* Телеметрия eCAN и фоновая выдача ответов логгера.
* UMP_CAN_service вызывается из main, не из приёмного IRQ.
/* Телеметрия eCAN и фоновая выдача ответов логгера.
* UMP_CAN_service вызывается из main, не из приёмного IRQ.
*/
void InitCan(int Port, int DevNum);
int CAN_send(int Port, int data[], int Addr); /* 0: занят, 1: кадр поставлен, 2: подтверждённые данные не изменились и 1,3 с ещё не прошли. */
void CAN_heartbeat_service(void); /* Планирует отдельный heartbeat по Timer1 с периодом одна секунда. */
void CAN_telemetry_service(void); /* В main обслуживает heartbeat, изменения данных и повтор каждого разрешённого пакета через 1,3 с. */
void CAN_send(int Port, int data[], int Addr);
extern int CAN_input_data[];
/* Неблокирующая передача ответов логгера отдельным CAN mailbox. */
/* Неблокирующая передача ответов логгера отдельным CAN mailbox. */
void UMP_CAN_service(void);

View File

@@ -1,10 +0,0 @@
#ifndef BALSAM_FIRMWARE_INFO_H /* Исключает повторное объявление адаптера паспорта при совместном подключении RS и CAN. */
#define BALSAM_FIRMWARE_INFO_H /* Обозначает единственный интерфейс firmware-info v1 приложения 166. */
#include "firmware_info_static.h" /* Использует тот же статический 12-словный контракт общей библиотеки, который подключён в 167. */
#define FIRMWARE_INFO_ADDRESS 0x1200U /* Адрес firmware-info v1 из протокола 167 и SetGUI; не пересекается с логгером 0x1000..0x117B. */
#define FIRMWARE_INFO_END (FIRMWARE_INFO_ADDRESS + FIRMWARE_INFO_STATIC_COUNT) /* Исключающая верхняя граница 0x120C охватывает ровно двенадцать 16-битных регистров. */
static unsigned int firmware_info_range(unsigned int address, unsigned int count) /* Проверяет только чтение внутри паспорта без переполнения 16-битной суммы адреса и длины. */
{ /* Вычитание выполняется после проверки адреса, поэтому не переполняется на C28x. */
return address >= FIRMWARE_INFO_ADDRESS && address < FIRMWARE_INFO_END && count > 0U && count <= FIRMWARE_INFO_END - address; /* Разрешает полное и частичное чтение, запрещая нулевую длину и пересечение границы паспорта. */
} /* Не меняет правила остальных технологических и служебных адресов. */
#endif /* Завершает адаптер, не добавляя изменяемое состояние или параметры EEPROM. */

View File

@@ -1,5 +1,5 @@
/* Контракт логгера с SetGUI: размер записи, версия и адреса.
* Чтение целого блока защищается от IRQ на стороне RS/CAN обработчика.
/* Контракт логгера с SetGUI: размер записи, версия и адреса.
* Чтение целого блока защищается от IRQ на стороне RS/CAN обработчика.
*/
#ifndef _LOG_TO_MEM
#define _LOG_TO_MEM
@@ -8,11 +8,11 @@
extern "C" {
#endif
/* Определениa длa работы логгера */
/* Определениa длa работы логгера */
#define LOG_PAGE_START 0x0200000
#define LOG_PAGE_LEN 0xFA00
extern int no_write, never_write; // Флаги, чтобы не писать (если что)
extern int no_write, never_write; // Флаги, чтобы не писать (если что)
typedef struct
{
@@ -25,25 +25,22 @@ typedef struct
extern LOG Log;
/* Запись словa в памать, где логи лежат */
/* Запись словa в памать, где логи лежат */
#define Log_to_mem(x) *(int *)(Log.Adres++) = x
/* Проверка границы памати дла логов */
/* Проверка границы памати дла логов */
#define Test_mem_limit(x) if(Log.Adres > (Log.Finis - (x))) Log.Adres = Log.Start
/* Очистка памати (обнуление) */
/* Очистка памати (обнуление) */
void clear_mem();
/* Логгер УМП: отдельное адресное пространство Modbus, без изменения
* 128 технологических регистров. Два банка по 800 записей:
* текущий кольцевой журнал и зафиксированный фрагмент отключения. */
/* Логгер УМП: отдельное адресное пространство Modbus, без изменения
* 128 технологических регистров. Два банка по 800 записей:
* текущий кольцевой журнал и зафиксированный фрагмент отключения. */
#define UMP_LOG_BASE 0x1000U
#define UMP_LOG_DATA 0x1010U
/* Формат v4 совпадает с 167: 12 слов по 16 бит содержат выбранные сигналы и нулевой резерв для парной передачи CAN. */
#define UMP_LOG_WORDS 12U
#define UMP_LOG_WORDS 40U
#define UMP_LOG_CAPACITY 800U
/* Онлайн-окно вмещает 120 слов полезных данных: при формате v4 это десять полных записей. */
#define UMP_LOG_LIVE_RECORDS (120U / UMP_LOG_WORDS)
#define UMP_LOG_LIVE 0x1100U
extern unsigned int ump_dac_ramp, ump_dac_duration, ump_dac_go10, ump_dac_stop10, ump_dac_position;
void ump_log_tick(void);

View File

@@ -1,17 +1,11 @@
/* Макросы обращаются к общей карте modbus, не к локальным данным.
* START/STOP читаются через volatile. Входящая запись слова 127
* должна пройти UMP_commands_write для фиксации фронта.
/* Макросы обращаются к общей карте modbus, не к локальным данным.
* START/STOP читаются через volatile. Входящая запись слова 127
* должна пройти UMP_commands_write для фиксации фронта.
*/
#ifndef PACKAGE
#define PACKAGE
/* Задаёт числовой код прямого порядка индикаторов, чтобы сравнение OBOROT в препроцессоре не заменяло неопределённое имя нулём. */
#define STRAIGHT 0
/* Отличающийся код обратного порядка выбирает ветку перестановки двух показаний в pulto.c. */
#define REVERSE 1
/* Выбирает исполнение 166: соответствующие калибровки и обратный порядок индикаторов задаются условными блоками ниже. */
#define BALSAM 166
#define BALSAM 165
#if BALSAM == 166
#define OBOROT REVERSE
@@ -90,13 +84,13 @@
#define bSecretBt Buttons.bit.bit1
#define bTermoCal Buttons.bit.bit2
#define Cancount (modbus+0x60) // пауза между I посылками CAN
#define Bright (modbus+0x62) // аркость сигнальных лампочек
#define Brightness modbus[0x62] // аркость сигнальных лампочек
#define Cancount (modbus+0x60) // пауза между I посылками CAN
#define Bright (modbus+0x62) // аркость сигнальных лампочек
#define Brightness modbus[0x62] // аркость сигнальных лампочек
/* Не врема!
#define DAC_go modbus[0x64] // начало зарада
#define DAC_stop modbus[0x65] // конец зарада
/* Не врема!
#define DAC_go modbus[0x64] // начало зарада
#define DAC_stop modbus[0x65] // конец зарада
*/
#define m_FAST 0
@@ -141,7 +135,7 @@
#define LastMode Modbus[126].all
/* Слово меняется из CAN/RS; чтение команды не должно кешироваться. */
/* Слово меняется из CAN/RS; чтение команды не должно кешироваться. */
#define Commands (((volatile WORDE *)modbus)[127].all)
#define cTestLamp Modbus[127].bit.bit0
#define cDefParam Modbus[127].bit.bit1

View File

@@ -1,34 +1,28 @@
/* Служебный CAN-протокол логгера УМП. Не использует адреса 0..127
* технологического обмена. Формат полезных данных совместим с порядком
* слов старого CAN: MDL (слова 1,2), затем MDH (маркер, слово 0).
* Запрос: маркер 0x1F00, слово 0 = token:8 | function:8,
* слова 1,2 = адрес расширения и число слов/записываемое значение.
* Ответы: маркер 0x1F00 + номер фрагмента, слово 0 = token:8 | error:8,
* слова 1,2 = два слова единого зафиксированного снимка.
* Биты маски записи в заголовке запроса равны нулю: старая плата
* игнорирует запрос, не изменяя технологические регистры. */
/* Служебный CAN-протокол логгера УМП. Не использует адреса 0..127
* технологического обмена. Формат полезных данных совместим с порядком
* слов старого CAN: MDL (слова 1,2), затем MDH (маркер, слово 0).
* Запрос: маркер 0x1F00, слово 0 = token:8 | function:8,
* слова 1,2 = адрес расширения и число слов/записываемое значение.
* Ответы: маркер 0x1F00 + номер фрагмента, слово 0 = token:8 | error:8,
* слова 1,2 = два слова единого зафиксированного снимка.
* Биты маски записи в заголовке запроса равны нулю: старая плата
* игнорирует запрос, не изменяя технологические регистры. */
#ifndef UMP_CAN_PROTOCOL_H
#define UMP_CAN_PROTOCOL_H
#include "log_to_mem.h"
#include "firmware_info.h" /* Использует то же окно firmware-info v1, что FC03, сохраняя остальной служебный CAN-протокол 166. */
#define UMP_CAN_MARKER 0x1F00U
#define UMP_CAN_MAX_WORDS 124U
/* Вызывающий код защищает весь снимок от IRQ. Функция не ждёт CAN и
* не пишет за пределами reply[124]. Ошибки возвращаются отдельным кадром. */
/* Вызывающий код защищает весь снимок от IRQ. Функция не ждёт CAN и
* не пишет за пределами reply[124]. Ошибки возвращаются отдельным кадром. */
static unsigned int ump_can_prepare(unsigned int function, unsigned int address,
unsigned int value, unsigned int *reply, unsigned int *error)
{
unsigned int i;
*error = 0;
if (function == 3) {
if (firmware_info_range(address, value)) { /* Признаёт только чтение внутри нового двенадцатисловного паспорта. */
for (i = 0; i < value; ++i) reply[i] = firmware_info_static_read(address - FIRMWARE_INFO_ADDRESS + i); /* Общий статический упаковщик выдаёт те же слова, что RS, без чтения архива логгера. */
return value; /* Передаёт паспорт существующему механизму фрагментов CAN без изменения mailbox, ID и повторов. */
} /* Все остальные запросы продолжают проходить прежнюю проверку окон логгера v4. */
if (!((address == UMP_LOG_BASE && value == 16) ||
/* Разрешает от одной до десяти целых 12-словных записей v4 в пределах 120 слов полезного окна CAN. */
(address == UMP_LOG_DATA && value >= UMP_LOG_WORDS && value <= 120U && value % UMP_LOG_WORDS == 0U) ||
(address == UMP_LOG_DATA && (value == 40 || value == 80 || value == 120)) ||
(address == UMP_LOG_LIVE && value == 124))) *error = 2;
else {
for (i = 0; i < value; i++) reply[i] = ump_log_read(address+i);
@@ -38,7 +32,7 @@ static unsigned int ump_can_prepare(unsigned int function, unsigned int address,
if (address < UMP_LOG_BASE || address > UMP_LOG_BASE+3) *error = 2;
else if (!ump_log_write(address, value)) *error = 3;
} else *error = 1;
/* Для записи возвращаем эхо адреса и значения, для ошибки — её контекст. */
/* Для записи возвращаем эхо адреса и значения, для ошибки — её контекст. */
reply[0] = address; reply[1] = value;
return 2;
}

View File

@@ -49,8 +49,6 @@ static unsigned int ump_can_length, ump_can_index, ump_can_reply_tag;
static unsigned int ump_can_waiting;
static unsigned long ump_can_sent_at;
#include "can_telemetry_impl.h" /* Определяет heartbeat, подтверждённый кэш и повтор данных через 1,3 с после объявления состояния штатного TX. */
void InitCan(int Port, int DevNum)
{
struct ECAN_REGS ECanShadow;
@@ -61,7 +59,6 @@ void InitCan(int Port, int DevNum)
long id = 0x80BA0000;
CAN_telemetry_init(); /* Сбрасывает кэш, очередь и секундный heartbeat до разрешения аппаратных прерываний. */
DevNum--;
if(DevNum<0)DevNum=0;
if(DevNum>15)DevNum=15;
@@ -141,8 +138,8 @@ void InitCan(int Port, int DevNum)
ECanMOTORegs->MOTO0 = 550000;
ECanMOTORegs->MOTO1 = 550000;
ECanRegs->CANTOC.all = 1;
ECanRegs->CANTOS.all = 0; // clear all time-out flags
/* До первой передачи отключает тайм-ауты всех mailbox; по SPRUI07, 13.8.18.3, сброс TOC снимает TOS и MTOF. */
ECanRegs->CANTOC.all = 0;
ECanRegs->CANTSC = 0; // clear time-out counter
ECanShadow.CANGIM.all = 0;
@@ -175,7 +172,49 @@ void InitCan(int Port, int DevNum)
/* При занятом MBOX0 возвращается без ожидания ACK в цикле.
* Это отличается от блокирующих UART/EEPROM операций main.
*/
/* CAN_send определён общим для этой единицы трансляции блоком can_telemetry_impl.h выше. */
void CAN_send(int Port, int data[], int Addr)
{
unsigned long hiword,loword;
volatile struct ECAN_REGS * ECanRegs;
volatile struct ECAN_MBOXES * ECanMboxes;
ECanRegs = &ECanaRegs;
ECanMboxes = &ECanaMboxes;
/* Не меняет данные mailbox 0, пока передача либо её аппаратная отмена ещё выполняется; бит 0 соответствует штатному TX. */
if ((ECanRegs->CANTRS.all | ECanRegs->CANTRR.all) & 1UL)
return; /* Сохраняет текущий кадр до завершения CANTRS/CANTRR, не ожидая аппаратного ответа в главном цикле. */
if(wait)
if(!(ECanRegs->CANTA.all & 1))
if(!(ECanRegs->CANAA.all & 1))
return;
ECanRegs->CANTA.all = 1;
ECanRegs->CANAA.all = 1;
hiword= ((((Uint32) Addr ) & 0xffff)<<16)| 0xE0000000 |
((((Uint32)data[Addr ]) & 0xffff) );
loword= ((((Uint32)data[Addr+1]) & 0xffff)<<16)|
((((Uint32)data[Addr+2]) & 0xffff) );
ECanMboxes->MBOX0.MDH.all = hiword;
ECanMboxes->MBOX0.MDL.all = loword;
EALLOW;
/* Отключает старый контроль mailbox 0 перед перезапуском счётчика; остальные mailbox используют собственный контроль. */
ECanRegs->CANTOC.all = 0;
/* Записывает ECanRegs->CANTSC значением 0; E CAN Regs CANTSC. */
ECanRegs->CANTSC = 0; // clear time-out counter
/* Заново разрешает тайм-аут штатного TX с порогом MOTO0, установленным в InitCan, только для следующего кадра. */
ECanRegs->CANTOC.all = 1UL;
EDIS;
ECanRegs->CANTRS.all = 1; // запустить передачу
wait=1;
led1_toggle();
}
void Handlai(volatile struct ECAN_MBOXES * ECanMboxes)
@@ -266,11 +305,10 @@ interrupt void CANa_reset_err(void)
if (ECanaRegs.CANTOS.all & 1UL)
{ /* Обрабатывает только штатный передатчик; служебный mailbox 2 контролируется отдельно в UMP_CAN_service. */
EALLOW; /* Разрешает изменение управляющего регистра eCAN перед снятием источника прерывания. */
ECanaRegs.CANTOC.all &= ~1UL; /* Снимает timeout только mailbox 0, сохраняя контроль служебных передач. */ /* Сброс TOC снимает TOS и MTOF согласно SPRUI07, 13.8.18.3; одной отмены TX недостаточно. */
ECanaRegs.CANTOC.all = 0; /* Сброс TOC снимает TOS и MTOF согласно SPRUI07, 13.8.18.3; одной отмены TX недостаточно. */
EDIS; /* Восстанавливает защиту регистров до выхода из ветви обработки тайм-аута. */
if (ECanaRegs.CANTRS.all & 1UL) /* Передача могла завершиться между чтением TOS и снятием контроля. */
ECanaRegs.CANTRR.all = 1UL; /* Запрашивает отмену только ещё занятого mailbox 0, не задерживая прерывание ожиданием CANAA. */
can_tx_failed = 1U; /* Не допускает записи повреждённого или отменённого кадра в подтверждённый кэш телеметрии. */
CanTimeOutErrorTR++; /* Считает обнаруженный тайм-аут один раз, исключая фантомные повторные входы PIE. */
} /* Следующая штатная передача сама заново включит TOC после сброса временного счётчика. */

View File

@@ -1,7 +1,7 @@
/* Программный канал индикации. digits/readr задают символы и выбор
* элементов, DCLK/DOUT и kanal_Send формируют последовательность GPIO.
* Это другой интерфейс, чем dCLK/dCS в DAC.c. Похожие имена не
* означают общий ЦАП или общий выход запуска нагрузки.
/* Программный канал индикации. digits/readr задают символы и выбор
* элементов, DCLK/DOUT и kanal_Send формируют последовательность GPIO.
* Это другой интерфейс, чем dCLK/dCS в DAC.c. Похожие имена не
* означают общий ЦАП или общий выход запуска нагрузки.
*/
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
@@ -20,6 +20,27 @@
int digits[16] = {63,6,91,79,102,109,125,7,127,111,64,0,0,0,121,0};
int readr[5] = {0x08,0x0C,0x10,0x00,0x04};
/* Ограничивает настройки таймингов компилятором TI C2000; модель на ПК не использует эти директивы. */
#if defined(__TI_COMPILER_VERSION__)
/* Сохраняет отдельный вызов переключения CLOCK: встраивание при -O3 сокращало удержание DATA после фронта. */
#pragma FUNC_CANNOT_INLINE(DCLK)
/* Возвращает генерацию DCLK как в образе от 06.10.2026: уровень 1 и приоритет скорости 3 сохраняют прежнюю последовательность инструкций GPIO. */
#pragma FUNCTION_OPTIONS(DCLK, "--opt_level=1 --opt_for_speed=3")
/* Сохраняет отдельный вызов переключения DATA, чтобы длительность импульсов не зависела от встраивания в передатчик. */
#pragma FUNC_CANNOT_INLINE(DOUT)
/* Возвращает генерацию DOUT с прежними параметрами -O1 и --opt_for_speed=3; значения взяты из старой конфигурации CCS. */
#pragma FUNCTION_OPTIONS(DOUT, "--opt_level=1 --opt_for_speed=3")
/* Не позволяет встроить разделитель пакетов в цикл передачи и изменить интервалы между переключениями линий. */
#pragma FUNC_CANNOT_INLINE(RESET)
/* Сохраняет прежнюю последовательность вызовов GPIO и задержек разделителя, используя настройки старого образа. */
#pragma FUNCTION_OPTIONS(RESET, "--opt_level=1 --opt_for_speed=3")
/* Сохраняет отдельный вызов передачи бита; протокол использует порядок и интервалы фронтов CLOCK и DATA. */
#pragma FUNC_CANNOT_INLINE(SENDBIT)
/* Закрепляет для передачи бита настройки из 167: -O1 и --opt_for_speed=3 сохраняют интервалы GPIO независимо от общего уровня оптимизации проекта. */
#pragma FUNCTION_OPTIONS(SENDBIT, "--opt_level=1 --opt_for_speed=3")
/* Завершает блок настроек TI, не изменяя формат пакетов и существующие счётчики задержек. */
#endif
void DCLK(int x)
{
if(x) GpioDataRegs.GPASET.bit.GPIO6=1;
@@ -88,7 +109,7 @@ void kanal_Send(int adr, long dat, int dot)
long Word,data,aliq_part,dg[4];
int i,j,bit,byt,addr,sgn=0,punkt=0,aliq_len=0,full_len, isdot=0;
if(adr>1) // Лампочки
if(adr>1) // Адреса каналов выше 1 передают готовую битовую маску ламп; каналы 0 и 1 требуют преобразования числа в сегменты.
{
Word =dat;
}
@@ -139,7 +160,7 @@ void kanal_Send(int adr, long dat, int dot)
for(i=3;i>0;i--)
{
if((dg[i]==0)&&(i!=dot))
dg[i]=0xF; // Это значит пусто
dg[i]=0xF; // Индекс 0xF таблицы digits задаёт пустой символ, скрывая ведущий ноль вне позиции десятичной точки.
else break;
}
@@ -148,7 +169,7 @@ void kanal_Send(int adr, long dat, int dot)
{
if( (dg[i]==0xF)||(i==3))
{
dg[i]=0xA; // Это значит минус
dg[i]=0xA; // Индекс 0xA таблицы digits задаёт знак минуса в свободном разряде отрицательного числа.
break;
} }

View File

@@ -1,8 +1,8 @@
/* Регистратор УМП во внешней памяти .logg: два банка по 800 записей
* из 12 слов формата v4 для SetGUI. Архив отключения не мешает дальнейшей записи.
* Писатели из main/CAN/таймера защищаются короткими секциями IRQ
* с восстановлением прежней маски. Архив не энергонезависимый.
* clear_mem выполняется при запуске до включения прерываний.
/* Регистратор УМП во внешней памяти .logg: два банка по 800 записей
* из 40 слов для SetGUI. Архив отключения не мешает дальнейшей записи.
* Писатели из main/CAN/таймера защищаются короткими секциями IRQ
* с восстановлением прежней маски. Архив не энергонезависимый.
* clear_mem выполняется при запуске до включения прерываний.
*/
#include "DSP2833x_Device.h"
#include "filter_bat2.h"
@@ -16,7 +16,7 @@
#include "log_to_mem.h"
int no_write = 0,
never_write = 0; // Изначально разрешает запись; установленный флаг прекращает сохранение снимков до следующего запуска регистратора.
never_write = 0; // Флаги, чтобы не писать (если что)
#pragma DATA_SECTION(logs_block,".logg");
unsigned int logs_block[0xFA00];
@@ -24,7 +24,7 @@ unsigned int logs_block[0xFA00];
LOG Log;
unsigned int flog=0;
// Очищение памати, где логи лежат
// Очищение памати, где логи лежат
void clear_mem()
{
unsigned long i;
@@ -37,27 +37,27 @@ void clear_mem()
for (i=Log.Start; i<Log.Finis; i++)
*(volatile int *)i = 0;
}
/* Два банка используют уже выделенную внешнюю RAM .logg (64000 слов).
* Активный банк кольцевой. Через 1000 мс после команды отключения он
* становится архивом, а запись продолжается во втором банке. Переключение
* занимает постоянное время: копирования 30 тысяч слов под запретом IRQ нет.
* Архив переживает разрыв RS-связи, но НЕ исчезновение питания платы. */
/* Два банка используют уже выделенную внешнюю RAM .logg (64000 слов).
* Активный банк кольцевой. Через 1000 мс после команды отключения он
* становится архивом, а запись продолжается во втором банке. Переключение
* занимает постоянное время: копирования 30 тысяч слов под запретом IRQ нет.
* Архив переживает разрыв RS-связи, но НЕ исчезновение питания платы. */
static volatile unsigned int ump_state, ump_count, ump_cursor, ump_next;
static volatile unsigned int ump_period = 20, ump_divider;
static volatile unsigned long ump_time, ump_sequence;
static unsigned int ump_bank, ump_archive_bank = 1;
static unsigned int ump_archive_count, ump_archive_next, ump_generation;
static unsigned int ump_source, ump_post, ump_post_dropped;
/* Хранит последние ЗПТ1/ЗПТ2 в вольтах из регистров ПМ67 306/9; явные нули попадают в инициализацию COFF до первого CAN-кадра. */
static unsigned int ump_pm67_voltage[2] = {0, 0};
static unsigned int ump_can_header, ump_can_data[3], ump_can_count;
static unsigned int ump_prev_commands, ump_prev_inputs, ump_prev_outputs;
/* Диагностика внутренних переменных разгона. Обновляется самим Load_runner,
* не пересчитывает и не изменяет управляющий алгоритм. */
extern int CanTimeOutErrorTR;
/* Диагностика внутренних переменных разгона. Обновляется самим Load_runner,
* не пересчитывает и не изменяет управляющий алгоритм. */
unsigned int ump_dac_ramp, ump_dac_duration, ump_dac_go10, ump_dac_stop10, ump_dac_position;
/* Логические уровни входов платы нагрузки: физический ноль означает 1.
* Чтение GPIO непосредственно здесь позволяет записать вход до следующего
* обновления регистра Inputs в основном цикле. */
/* Логические уровни входов платы нагрузки: физический ноль означает 1.
* Чтение GPIO непосредственно здесь позволяет записать вход до следующего
* обновления регистра Inputs в основном цикле. */
static unsigned int ump_inputs(void)
{
return (!GpioDataRegs.GPADAT.bit.GPIO6) |
@@ -65,7 +65,7 @@ static unsigned int ump_inputs(void)
((!GpioDataRegs.GPADAT.bit.GPIO11) << 2);
}
/* START/STOP активны единицей; RES_OUT_2 активен физическим нулём. */
/* START/STOP активны единицей; RES_OUT_2 активен физическим нулём. */
static unsigned int ump_outputs(void)
{
return GpioDataRegs.GPBDAT.bit.GPIO62 |
@@ -73,62 +73,62 @@ static unsigned int ump_outputs(void)
((!GpioDataRegs.GPADAT.bit.GPIO3) << 2);
}
/* Вызывается только внутри короткой критической секции. Все три источника
* записей (main, CAN ISR, timer ISR) используют одну блокировку IRQ с
* восстановлением прежнего состояния, включая вызовы из прерывания. */
/* Вызывается только внутри короткой критической секции. Все три источника
* записей (main, CAN ISR, timer ISR) используют одну блокировку IRQ с
* восстановлением прежнего состояния, включая вызовы из прерывания. */
static void ump_capture(unsigned int reason)
{
/* Указывает на начало одной 12-словной записи во внешней RAM; volatile сохраняет фактические обращения к памяти. */
volatile unsigned int *record;
/* Сохраняет полное слово команд для прежней логики триггера; в запись попадут только биты пуска и сброса. */
unsigned int commands;
volatile unsigned int *p;
unsigned int i, commands;
if (ump_state != 1 || Desk != dsk_LOAD || READY) return;
if (no_write || never_write) { ump_state = 3; return; }
commands = Commands;
/* Отключение: фронт cUMPreset (бит 6) либо спад cUMPstart (бит 7).
* Повторные команды не сдвигают конец уже начатого постинтервала. */
/* Отключение: фронт cUMPreset (бит 6) либо спад cUMPstart (бит 7).
* Повторные команды не сдвигают конец уже начатого постинтервала. */
if (!ump_post && (((commands & 0x40) && !(ump_prev_commands & 0x40)) ||
(!(commands & 0x80) && (ump_prev_commands & 0x80)))) {
/* Половину банка резервируем под постинтервал. Даже при периоде
* 1 мс или шквале CAN событие отключения не вытесняется кольцом. */
/* Половину банка резервируем под постинтервал. Даже при периоде
* 1 мс или шквале CAN событие отключения не вытесняется кольцом. */
if (ump_count >= UMP_LOG_CAPACITY/2) ump_count = UMP_LOG_CAPACITY/2-1;
ump_post_dropped = 0;
ump_post = 1000;
reason |= 0x100;
}
/* Последнее место оставляем для снимка конца постинтервала. При
* переполнении честно отмечаем усечение битом 0x200 в конечной записи. */
/* Последнее место оставляем для снимка конца постинтервала. При
* переполнении честно отмечаем усечение битом 0x200 в конечной записи. */
if (ump_post && ump_count >= UMP_LOG_CAPACITY-1) {
ump_post_dropped = 1;
return;
}
if (reason == 5 && ump_post_dropped) reason |= 0x200;
/* Выбирает слот банка по числу 16-битных слов записи; ёмкость 800 записей сохранена для исполнения 166. */
record = logs_block + ((unsigned long)ump_bank * UMP_LOG_CAPACITY + ump_next) * UMP_LOG_WORDS;
/* Слова 0/1 передают 32-битное время в миллисекундах младшей половиной вперёд, как в формате v4 исполнения 167. */
record[0] = (unsigned int)ump_time; record[1] = (unsigned int)(ump_time >> 16);
/* Слово 2 содержит событие, а маска 0xC0 оставляет в слове 3 только назначенные протоколом биты 7/6 пуска и сброса УМП. */
record[2] = reason; record[3] = commands & 0xC0U;
/* Слова 4/5 сохраняют фактические логические входы и выходы нагрузки для дискретных дорожек SetGUI. */
record[4] = ump_inputs(); record[5] = ump_outputs();
/* Слово 6 содержит расчётное задание тока регистра 0x1C в единицах 0,1 мА; физическое измерение тока здесь не выполняется. */
record[6] = modbus[0x1C];
/* Слова 7/8 содержат штатные фиксированные уставки разгона 166: начальные 6 мА и конечные 16 мА. */
record[7] = 6; record[8] = 16;
/* Слова 9/10 передают последние ЗПТ1/ЗПТ2 в вольтах из CAN-индексов 305/8, соответствующих регистрам ПМ67 306/9. */
record[9] = ump_pm67_voltage[0]; record[10] = ump_pm67_voltage[1];
/* Нулевое слово 11 дополняет запись до чётной длины: служебный CAN передаёт данные парами 16-битных слов. */
record[11] = 0;
/* Обновляет полные команды и фактические дискреты для обнаружения следующих событий, сохраняя алгоритм триггера 166. */
ump_prev_commands = commands; ump_prev_inputs = record[4]; ump_prev_outputs = record[5];
p = logs_block + ((unsigned long)ump_bank * UMP_LOG_CAPACITY + ump_next) * UMP_LOG_WORDS;
p[0] = (unsigned int)ump_time; p[1] = (unsigned int)(ump_time >> 16);
p[2] = reason; p[3] = commands;
p[4] = ump_inputs(); p[5] = ump_outputs();
p[6] = (unsigned int)IMPowse; p[7] = (unsigned int)WAKEpowse;
p[8] = modbus[0x7B]; p[9] = modbus[0x1C];
for (i = 0; i < 4; i++) { p[10+i] = sens_data[i]; p[14+i] = modbus[i]; }
p[18] = ump_can_header;
p[19] = ump_can_data[0]; p[20] = ump_can_data[1]; p[21] = ump_can_data[2];
p[22] = ump_can_count; p[23] = CanTimeOutErrorTR;
/* Исходные уставки — мА; эффективные уставки после ограничений — 0.1 мА.
* p[8] — отправленный код, а p[9] — расчётное задание, НЕ измерение тока. */
/* 166: fixed ramp endpoints; no configurable DAC_go/DAC_stop. */
p[24] = 6; p[25] = 16;
p[26] = DAC_min(); p[27] = DAC_max(); p[28] = DAC_cal();
p[29] = DAC_04; p[30] = DAC_20;
p[31] = ump_dac_ramp; p[32] = ump_dac_duration;
p[33] = ump_dac_go10; p[34] = ump_dac_stop10; p[35] = ump_dac_position;
for (i = 0; i < 4; i++) p[36+i] = adc_table_lem[i];
ump_prev_commands = commands; ump_prev_inputs = p[4]; ump_prev_outputs = p[5];
ump_next++;
if (ump_next == UMP_LOG_CAPACITY) ump_next = 0;
if (ump_count < UMP_LOG_CAPACITY) ump_count++;
ump_sequence++;
}
/* Завершённый банк больше не изменяется до следующего отключения.
* Номер поколения позволяет GUI обнаружить замену архива во время чтения. */
/* Завершённый банк больше не изменяется до следующего отключения.
* Номер поколения позволяет GUI обнаружить замену архива во время чтения. */
static void ump_freeze(void)
{
ump_archive_bank = ump_bank;
@@ -152,8 +152,8 @@ void ump_log_tick(void)
__restore_interrupts(saved);
}
/* Дополнительный снимок только при изменении команд, входов или выходов.
* Это не заменяет периодическую запись аналоговых величин в таймере. */
/* Дополнительный снимок только при изменении команд, входов или выходов.
* Это не заменяет периодическую запись аналоговых величин в таймере. */
void ump_log_io(void)
{
unsigned int saved;
@@ -164,34 +164,17 @@ void ump_log_io(void)
__restore_interrupts(saved);
}
/* Обновляет напряжения из принятых слов CAN даже до запуска записи;
* активный регистратор дополнительно сохраняет событие приёма. */
/* Вызывается после применения слов принятого CAN-кадра: снимок содержит
* новые команды, а также исходный адрес/маску и все три слова кадра. */
void ump_log_can(unsigned int header, unsigned int a, unsigned int b, unsigned int c)
{
/* Хранит прежнюю маску IRQ, индекс слова, текущий CAN-адрес, маску разрешения и три принятых 16-битных значения. */
unsigned int saved, word_index, address, mask, can_words[3];
/* Напряжения и журнал принадлежат плате нагрузки; другие аппаратные режимы их не обновляют. */
if (Desk != dsk_LOAD) return;
/* Исключает смешивание обновления напряжений с периодическим снимком таймера и восстанавливает прежние IRQ в конце. */
unsigned int saved;
if (ump_state != 1 || Desk != dsk_LOAD) return;
saved = __disable_interrupts();
/* Располагает три принятых слова в том же порядке, в котором заголовок задаёт их последовательные адреса. */
can_words[0] = a; can_words[1] = b; can_words[2] = c;
/* Младшие 13 бит заголовка задают нулевой индекс первого регистра CAN; верхние три бита являются масками слов. */
address = header & 0x1FFFU;
/* Старший бит 0x8000 разрешает первое слово, далее используются биты 0x4000 и 0x2000. */
mask = 0x8000U;
/* Продвигает адрес и маску для всех трёх позиций, включая пропущенные слова, чтобы не перепутать регистры ПМ67. */
for (word_index = 0; word_index < 3; word_index++, address++, mask >>= 1) {
/* Не использует содержимое позиции, если соответствующий бит заголовка не подтверждает её передачу. */
if (!(header & mask)) continue;
/* Индекс 305 соответствует регистру 306 ПМ67: суммарное напряжение ЗПТ1 передаётся в целых вольтах. */
if (address == 305U) ump_pm67_voltage[0] = can_words[word_index];
/* Индекс 8 соответствует регистру 9 ПМ67: суммарное напряжение ЗПТ2 передаётся в целых вольтах. */
if (address == 8U) ump_pm67_voltage[1] = can_words[word_index];
} /* Завершает разбор трёх слов CAN после обновления только подтверждённых напряжений. */
/* Сохраняет компактный снимок только при активной записи и готовности 166, которые проверяет ump_capture. */
ump_can_header = header;
ump_can_data[0] = a; ump_can_data[1] = b; ump_can_data[2] = c;
ump_can_count++;
ump_capture(2);
/* Возвращает исходную маску IRQ, в том числе при вызове из обработчика CAN. */
__restore_interrupts(saved);
}
@@ -202,8 +185,8 @@ void ump_log_rs(void)
__restore_interrupts(saved);
}
/* Управление журналом отделено от технологических команд: начало записи
* НЕ запускает УМП. Выбор архива не останавливает текущую запись. */
/* Управление журналом отделено от технологических команд: начало записи
* НЕ запускает УМП. Выбор архива не останавливает текущую запись. */
int ump_log_write(unsigned int address, unsigned int value)
{
unsigned int saved, valid = 1;
@@ -228,8 +211,8 @@ int ump_log_write(unsigned int address, unsigned int value)
return valid;
}
/* Преобразование хронологического индекса в физическую позицию кольца.
* Для архива сохранены собственные count/next, независимые от живого банка. */
/* Преобразование хронологического индекса в физическую позицию кольца.
* Для архива сохранены собственные count/next, независимые от живого банка. */
static unsigned int ump_word(unsigned int archived, unsigned int record, unsigned int word)
{
unsigned int count = archived ? ump_archive_count : ump_count;
@@ -242,17 +225,16 @@ static unsigned int ump_word(unsigned int archived, unsigned int record, unsigne
return logs_block[((unsigned long)bank * UMP_LOG_CAPACITY + slot) * UMP_LOG_WORDS + word];
}
/* Весь Modbus-блок читается вызывающим кодом под одной блокировкой IRQ.
* Поэтому timestamp/данные и заголовок онлайн-блока относятся к одному
* состоянию кольца, даже если в этот момент приходит новая CAN-команда. */
/* Весь Modbus-блок читается вызывающим кодом под одной блокировкой IRQ.
* Поэтому timestamp/данные и заголовок онлайн-блока относятся к одному
* состоянию кольца, даже если в этот момент приходит новая CAN-команда. */
unsigned int ump_log_read(unsigned int address)
{
unsigned int offset, count;
unsigned long first;
switch (address) {
case UMP_LOG_BASE: return 0x554D;
/* Версия 4 сообщает SetGUI о 12-словной записи, исключая её ошибочный разбор как прежних 40 слов v2. */
case UMP_LOG_BASE+1: return 4;
case UMP_LOG_BASE+1: return 2;
case UMP_LOG_BASE+2: return ump_source ? 3 : ump_state;
case UMP_LOG_BASE+3: return ump_source ? ump_archive_count : ump_count;
case UMP_LOG_BASE+4: return UMP_LOG_WORDS;
@@ -272,11 +254,10 @@ unsigned int ump_log_read(unsigned int address)
offset = address - UMP_LOG_DATA;
return ump_word(ump_source, ump_cursor + offset / UMP_LOG_WORDS, offset % UMP_LOG_WORDS);
}
/* Последние десять записей для онлайн-графика, без остановки и без
* изменения курсора архивного чтения. Первые два слова — их номер. */
/* Последние три записи для онлайн-графика, без остановки и без
* изменения курсора архивного чтения. Первые два слова — их номер. */
if (address >= UMP_LOG_LIVE && address < UMP_LOG_LIVE+124) {
/* Ограничивает число целых записей ёмкостью 120-словного окна: формат v4 позволяет передать до десяти снимков. */
count = ump_count < UMP_LOG_LIVE_RECORDS ? ump_count : UMP_LOG_LIVE_RECORDS;
count = ump_count < 3 ? ump_count : 3;
first = ump_sequence - count;
offset = address - UMP_LOG_LIVE;
if (offset == 0) return (unsigned int)first;

View File

@@ -1,8 +1,8 @@
/* Инициализация и фоновый цикл платы. После настройки GPIO, обмена,
* параметров и ЦАП UMP_pulse_init разрешает свежие команды СИФУ.
* В main остаются телеметрия, EEPROM и разбор готовых RS-пакетов.
* CAN-пуск и окончание импульса работают в IRQ, не ожидая main.
* RS-команда ещё должна пройти проверку CRC в get_command из main.
/* Инициализация и фоновый цикл платы. После настройки GPIO, обмена,
* параметров и ЦАП UMP_pulse_init разрешает свежие команды СИФУ.
* В main остаются телеметрия, EEPROM и разбор готовых RS-пакетов.
* CAN-пуск и окончание импульса работают в IRQ, не ожидая main.
* RS-команда ещё должна пройти проверку CRC в get_command из main.
*/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
@@ -43,7 +43,8 @@ void timer_Init()
void main()
{
int i,j; /* Счётчики остались для прежних операций main; выбор CAN-пакетов перенесён в независимый сервис. */
int i,j,mask;
static unsigned int cancount[2], cancell[2];
RS_DATA * rs;
int presbyt=0;
@@ -99,7 +100,7 @@ void main()
if(Desk==dsk_LOAD)
Setup_DAC_time();
/* Завершена начальная настройка: только теперь разрешаем дискреты. */
/* Завершена начальная настройка: только теперь разрешаем дискреты. */
UMP_pulse_init();
LastMode = Mode;
@@ -109,9 +110,32 @@ void main()
while(1)
{
/* Чтение логгера по CAN не блокирует цикл управления. */
/* Чтение логгера по CAN не блокирует цикл управления. */
UMP_CAN_service();
CAN_telemetry_service(); /* Обслуживает heartbeat 1 Гц, изменения масок данных и индивидуальные повторы 1,3 с без ожидания CAN ACK. */
if(CanGO)
{
CanGO=0;
for(i=0;i<2;i++)
if(Cancount[i])
if(++cancount[i] >= Cancount[i])
{
cancount[i] = 0;
while(1)
{
if(cancell[i] >= 0x80) cancell[i]=0;
mask = Maska[i][cancell[i]/16] >> (cancell[i]%16);
if(!mask) cancell[i] = (cancell[i] + 0x10) & 0xFFF0;
else
{
while(!(mask & 1))
{
cancell[i]++; mask >>= 1;
}
break;
} }
CAN_send(0,modbus,cancell[i]);
cancell[i]+=3;
} }
if(cDefParam)
{
@@ -145,10 +169,10 @@ void main()
presbyt = bTermoCal;
}
/* START/STOP исполняются при приёме CAN/RS, снимаются таймером.
* Здесь больше нет опроса уровня: main может ожидать EEPROM. */
/* START/STOP исполняются при приёме CAN/RS, снимаются таймером.
* Здесь больше нет опроса уровня: main может ожидать EEPROM. */
/* Снимок после обновления входов и формирования пусковых выходов. */
/* Снимок после обновления входов и формирования пусковых выходов. */
ump_log_io();
for(i=0;i<2;i++)
@@ -164,16 +188,16 @@ void main()
led2_toggle();
switch(j)
{
case CMD_INIT: init(rs); break; // начальные установки
case CMD_INITLOAD: initload(rs); break; // настройка загрузки
case CMD_RUN: run(rs); break; // загрузить блок
case CMD_LOAD: load(rs); break; // загрузить блок
case CMD_PEEK: peek(rs); break; // прочитать ачейку памати
case CMD_POKE: poke(rs); break; // записать в ачейку памати
case CMD_UPLOAD: upload(rs); break; // передать блок памати
case CMD_EXTEND: extendbios(rs); break; // расширенные команды дла биоса
case CMD_INIT: init(rs); break; // начальные установки
case CMD_INITLOAD: initload(rs); break; // настройка загрузки
case CMD_RUN: run(rs); break; // загрузить блок
case CMD_LOAD: load(rs); break; // загрузить блок
case CMD_PEEK: peek(rs); break; // прочитать ачейку памати
case CMD_POKE: poke(rs); break; // записать в ачейку памати
case CMD_UPLOAD: upload(rs); break; // передать блок памати
case CMD_EXTEND: extendbios(rs); break; // расширенные команды дла биоса
case CMD_TFLASH: tflash(rs); break; // прошить TMS
case CMD_TFLASH: tflash(rs); break; // прошить TMS
case CMD_MODBUS_3: ReceiveCommandModbus3(rs); break;
case CMD_MODBUS_6: ReceiveCommandModbus6(rs); break;

View File

@@ -1,8 +1,8 @@
/* Измерения, диагностика и периодическое обслуживание. ADC ISR
* поставляет выборки; таймер 1 обслуживает индикацию и телеметрию.
* Для dsk_LOAD он сначала завершает импульс СИФУ, затем вызывает
* расчёт ЦАП и снимок логгера. Частоты находятся в measure.h;
* при их изменении требуется пересчитать зависимые интервалы.
/* Измерения, диагностика и периодическое обслуживание. ADC ISR
* поставляет выборки; таймер 1 обслуживает индикацию и телеметрию.
* Для dsk_LOAD он сначала завершает импульс СИФУ, затем вызывает
* расчёт ЦАП и снимок логгера. Частоты находятся в measure.h;
* при их изменении требуется пересчитать зависимые интервалы.
*/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
@@ -20,13 +20,13 @@
#include "message.h"
#include "log_to_mem.h"
#include <math.h> // Это чтобы мерить амплитуду! sqrt без этого будет крив!!!
#include <math.h> // Это чтобы мерить амплитуду! sqrt без этого будет крив!!!
unsigned int CanPowse=CANPOWSE,CanGO=0;
unsigned int Maska[2][8];
int TPL_CANS=0; // Количество температурных каналов
int TPL_CANS=0; // Количество температурных каналов
int tpl_cans=0;
int cal_addr=0;
@@ -53,23 +53,23 @@ int Curr_Edge;
float powK[] =
{
0.127, // питание 380В
0.127, // питание 380В
0.127, // питание 380В
0.127, // питание 380В
1.000, // ток
1.000, // напражение
1.000, // ток
1.000, // напражение
0.0076, // питание 31В
0.0076, // питание 24В
0.0076, // питание 24В
0.0076, // питание 15В
1.0000, // термодатчик мелкосхема
0.0076, // питание 31В
0.0076, // питание 24В
0.0076, // питание 24В
0.0076, // питание 15В
1.0000, // термодатчик мелкосхема
};
FILTERBAT def_FILTERBAT = DEF_FILTERBAT;
FILTERBAT adc_filter[20+4]; // макс.TPL*2 + 4 калибр
FILTERBAT out_filter[20+4]; // макс.TPL*2 + 4 напраж
FILTERBAT zer_filter[4]; // 4 напраж
FILTERBAT adc_filter[20+4]; // макс.TPL*2 + 4 калибр
FILTERBAT out_filter[20+4]; // макс.TPL*2 + 4 напраж
FILTERBAT zer_filter[4]; // 4 напраж
long sens_count[24+4];
@@ -79,7 +79,7 @@ float K_T1,K_T2;
interrupt void cpu_timer1_isr_SENS(void);
/********************************************************************/
/* Расчет модула тока из показаний двух фаз */
/* Расчет модула тока из показаний двух фаз */
/********************************************************************/
float im_calc(float ia,float ib)
{
@@ -115,8 +115,8 @@ interrupt void cpu_timer1_isr_SENS(void)
int TST;
#define err_lamp work_lamp // это дла шкапа,
#define alm_lamp heat_lamp // чтоб было понатнее
#define err_lamp work_lamp // это дла шкапа,
#define alm_lamp heat_lamp // чтоб было понатнее
EALLOW;
CpuTimer1.InterruptCount++;
@@ -140,10 +140,10 @@ interrupt void cpu_timer1_isr_SENS(void)
if(Desk==dsk_LOAD)
{
/* Снять истёкший импульс до расчёта ЦАП, независимо от main. */
/* Снять истёкший импульс до расчёта ЦАП, независимо от main. */
UMP_pulse_tick();
Load_runner();
/* База времени 1 мс; снимок содержит результат текущего шага ЦАП. */
/* База времени 1 мс; снимок содержит результат текущего шага ЦАП. */
ump_log_tick();
return;
}
@@ -156,9 +156,9 @@ interrupt void cpu_timer1_isr_SENS(void)
((Desk==dsk_COMM) && heat_lamp) ) set_RES_OUT_1();
else clear_RES_OUT_1();
if(work_lamp) set_LED_OUT_1(); // Она же err_lamp дла шкапа
if(work_lamp) set_LED_OUT_1(); // Она же err_lamp дла шкапа
else clear_LED_OUT_1();
if(heat_lamp) set_LED_OUT_2(); // Она же alm_lamp дла шкапа
if(heat_lamp) set_LED_OUT_2(); // Она же alm_lamp дла шкапа
else clear_LED_OUT_2();
}
@@ -360,37 +360,37 @@ void Init_packMask()
switch(Desk)
{
case dsk_EPLT:
Maska[m_SLOW][0]|= 0x000F; // Полученное
Maska[m_SLOW][6]|= 0x01FC; // Яркость ламп и одиночество
Maska[m_FAST][0]|= 0x000F; // Возвращает R0–R3 задатчика в быстром потоке: маска 0x000F выбирает четыре младших регистра, чтобы подтверждение R3 для ПМ67 не ожидало медленного обхода параметров.
Maska[m_SLOW][6]|= 0x01FC; // Яркость ламп и одиночество
break;
default:
for(i=0;i<can;i++)
{
Maska[m_FAST][ i /16]|=(1<<( i %16)); // Диагностика
Maska[m_FAST][(i+24)/16]|=(1<<((i+24)%16)); // Показаниа
Maska[m_FAST][ i /16]|=(1<<( i %16)); // Диагностика
Maska[m_FAST][(i+24)/16]|=(1<<((i+24)%16)); // Показаниа
}
for(i=0;i<3; i++)
Maska[m_SLOW][i+3] = Maska[m_FAST][i]; // Уставки
Maska[m_SLOW][i+3] = Maska[m_FAST][i]; // Уставки
if(Desk==dsk_BKSD) Maska[m_SLOW][7]|= 0x0300; // Калибровка термодатчиков
if(Desk==dsk_BKSD) Maska[m_SLOW][7]|= 0x0300; // Калибровка термодатчиков
else
Maska[m_SLOW][7]|= 0x0F00; // Калибровка термодатчиков
Maska[m_SLOW][7]|= 0x0F00; // Калибровка термодатчиков
if(Kurrent)
{ Maska[m_SLOW][7]|= 0x000F; // Ноль датчиков напражениа
Maska[m_SLOW][7]|= 0x00F0; // Калибровка датчиков напражениа
Maska[m_FAST][6]|= 0x3F00; // Фазные значениа
{ Maska[m_SLOW][7]|= 0x000F; // Ноль датчиков напражениа
Maska[m_SLOW][7]|= 0x00F0; // Калибровка датчиков напражениа
Maska[m_FAST][6]|= 0x3F00; // Фазные значениа
}
if(Desk==dsk_LOAD)
{ Maska[m_SLOW][7]|= 0x0030; // Калибровка ЦАП
Maska[m_FAST][7]|= 0xE000; // Дублировать команды
Maska[m_FAST][1]|= 0x3000; // Управлающий ток СИФУ
{ Maska[m_SLOW][7]|= 0x0030; // Калибровка ЦАП
Maska[m_FAST][7]|= 0xE000; // Дублировать команды
Maska[m_FAST][1]|= 0x3000; // Управлающий ток СИФУ
} }
Maska[m_FAST][1]|= 0x0001; // Дискретные входы
Maska[m_SLOW][6]|= 0x0007; // Яркость ламп и периоды посылок
Maska[m_SLOW][7]|= 0xE000; // Адрес, команды, и чтоб не вылазило
Maska[m_FAST][1]|= 0x0001; // Дискретные входы
Maska[m_SLOW][6]|= 0x0007; // Яркость ламп и периоды посылок
Maska[m_SLOW][7]|= 0xE000; // Адрес, команды, и чтоб не вылазило
}
int er_anal(int term, long * count, long edge, int pre)
@@ -449,26 +449,26 @@ void Current_count(int chan)
modbus[0x64+chan] = Numb;
// Средний (он же нулевой) уровень показаний АЦП
// Средний (он же нулевой) уровень показаний АЦП
zer_count[chan] += (Numb-zer_count[chan])/(5.0 * ADC_FREQ);
Zero_lev[chan] = (int)(filterbat(&zer_filter[chan],zer_count[chan]));
// Мгновенное значение фазы
// Мгновенное значение фазы
Current = (Numb - Zero_lev[chan]) * powK[chan];
// Действующее (среднеквадратичное) значение фазы
// Действующее (среднеквадратичное) значение фазы
lev_quadr[chan] += ((Current*Current)-lev_quadr[chan])/(1.0 * ADC_FREQ);
lev_count[chan] = sqrt(lev_quadr[chan]);
// Запомним
// Запомним
if(ist)
{
if(Desk==dsk_COMM) aCurrent =-Current;
else aCurrent = Current; // Запомнили мгновенное значение - дла амплитуды
else aCurrent = Current; // Запомнили мгновенное значение - дла амплитуды
}
else
{
// Вычисление амплитуды
// Вычисление амплитуды
Amplitude = filterbat(&out_filter[sens], im_calc(Current,aCurrent));
if(sens_type[chan]==VOLTAGE)
@@ -485,7 +485,7 @@ modbus[0x64+chan] = Numb;
sens_data[sens-1] = Level;
}
// Действующее (среднеквадратичное) значение третьей фазы
// Действующее (среднеквадратичное) значение третьей фазы
Numb =-Current-aCurrent;
lev_quadr[thrd] += (Numb*Numb-lev_quadr[thrd])/(1.0 * ADC_FREQ);
lev_count[thrd] = sqrt(lev_quadr[thrd]);
@@ -511,12 +511,12 @@ modbus[0x68+fazz+1] = lev_count[chan];
modbus[0x68+fazz+2] = lev_count[thrd];
}
// Это максимальное
// Это максимальное
Numb = lev_count[chan];
if(Numb<lev_count[pair]) Numb = lev_count[pair];
if(Numb<lev_count[thrd]) Numb = lev_count[thrd];
// Зашиты!
// Зашиты!
error.all = 0;
no_ignor = !sens_error[chan].bit.Ignor;
@@ -659,7 +659,7 @@ void Temper_count(int chan)
if(chan>tpl_cans) return;
// Kun именно чтоб скомпенсировать нечетность
// Kun именно чтоб скомпенсировать нечетность
if(kun&1) Numb = (Numb-K300_2)*K_T2+ZERO;
else Numb = (Numb-K300_1)*K_T1+ZERO;
}
@@ -706,7 +706,7 @@ sens_data[chan+8] = adc_table_tpl[chan];
error.all = 0;
no_ignor = !sens_error[chan].bit.Ignor;
// Обрыв или неисправность
// Обрыв или неисправность
if(Temper==-20 || Temper==200)
{
error.bit.Tear = 1;
@@ -714,7 +714,7 @@ sens_data[chan+8] = adc_table_tpl[chan];
}
else
// Перегрев
// Перегрев
if(((Temper>sens_hi_edge[chan]-Cooling) && (sens_error[chan].bit.Hyper)) ||
(Temper>sens_hi_edge[chan]) )
{
@@ -724,7 +724,7 @@ sens_data[chan+8] = adc_table_tpl[chan];
}
else
// Предупреждение по температуре
// Предупреждение по температуре
if(Temper>sens_lo_edge[chan])
{
error.bit.Over = 1;
@@ -796,7 +796,7 @@ void Power_count(int chan)
error.bit.Stop = 1;
} }
/* Повышенное напражение
/* Повышенное напражение
if(Power > sens_hi_edge[chan])
{
error.bit.Hyper = 1;

View File

@@ -1,8 +1,8 @@
/* Параметры и обработчики Modbus после проверки CRC. Массив modbus
* содержит обычные адреса 0..127, область 0x1000 обслуживает логгер.
* EEPROM хранит параметры, но не является источником START/STOP:
* при загрузке сохраняется последняя живая команда нагрузки.
* Функции вызываются из main; EEPROM блокирует этот контекст.
/* Параметры и обработчики Modbus после проверки CRC. Массив modbus
* содержит обычные адреса 0..127, область 0x1000 обслуживает логгер.
* EEPROM хранит параметры, но не является источником START/STOP:
* при загрузке сохраняется последняя живая команда нагрузки.
* Функции вызываются из main; EEPROM блокирует этот контекст.
*/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "package.h"
@@ -21,12 +21,9 @@
#include "ecan.h"
#include "pulto.h"
#include "spise2p.h"
#include "include/balsam167_eeprom_block.h" /* Использует общий с 167 контракт FC03: окно 0x8000..0xBFFF, до 120 слов AT25256B за запрос. */
#include "caliber.h"
#include "log_to_mem.h"
#include "firmware_info.h" /* Подключает адрес и границы неизменяемого паспорта 0x1200, совместимого с проверкой прошивки в 167. */
#include "../src/firmware_info_static.c" /* Собирает общие 12 слов firmware-info через include-путь firmware-info/include; как в 167, копии упаковщика в проекте нет. */
#include "DAC.h"
int modbus[ANSWER_LEN+1];
@@ -50,7 +47,7 @@ void Default_params()
for(i=0;i<ANSWER_LEN;i++)
{
/* Команда нагрузки не является параметром по умолчанию. */
/* Команда нагрузки не является параметром по умолчанию. */
if (Desk != dsk_LOAD || i != 127) Modbus[i].all = 0;
}
@@ -59,17 +56,17 @@ void Default_params()
if(Desk == dsk_EPLT)
{
Cancount[m_FAST] = 10; // Задаёт для пульта быстрый CAN-пакет раз в 10 базовых интервалов CANPOWSE по 20 мс, то есть примерно 200 мс; это значение по умолчанию, которое могут заменить параметры EEPROM.
Cancount[m_FAST] = 2; // пауза между посылками CAN
for(i=0;i<6;i++) Bright[i] = bright[i];
}
else
{
Cancount[m_FAST] = 10; // пауза между посылками CAN
Cancount[m_FAST] = 10; // пауза между посылками CAN
Brightness = 8;
}
Cancount[m_SLOW] = 50; // Задаёт медленную CAN-рассылку раз в 50 базовых интервалов CANPOWSE по 20 мс, то есть примерно раз в секунду; это значение по умолчанию могут заменить параметры EEPROM.
Cancount[m_SLOW] = 101; // пауза между посылками CAN
if(Kalibro)
{
@@ -129,9 +126,9 @@ void Default_params()
DAC_20 = Caliber[6];
DAC_04 = Caliber[7];
/* Не врема!
DAC_go = 6; // начало зарада
DAC_stop= 15; // конец зарада
/* Не врема!
DAC_go = 6; // начало зарада
DAC_stop= 15; // конец зарада
*/
}
@@ -142,9 +139,9 @@ void Default_params()
Init_sensors_more();
}
/* CRC и Mode проверяются до применения EEPROM. Слово 127
* нагрузки не копируется даже временно: ISR не должен увидеть
* старый START из EEPROM либо потерять текущую CAN-команду.
/* CRC и Mode проверяются до применения EEPROM. Слово 127
* нагрузки не копируется даже временно: ISR не должен увидеть
* старый START из EEPROM либо потерять текущую CAN-команду.
*/
void Load_params()
{
@@ -156,7 +153,7 @@ void Load_params()
(crc !=0xFFFF) &&
(Mode == param[126]))
{
/* Не восстанавливать START/STOP из EEPROM даже на один тик ISR. */
/* Не восстанавливать START/STOP из EEPROM даже на один тик ISR. */
for(i=0;i<ANSWER_LEN;i++)
if (Desk != dsk_LOAD || i != 127) modbus[i] = param[i];
UMP_reset_parameter_commands();
@@ -172,9 +169,9 @@ void Load_params()
Caliber_time = 0xFFFF;
} } }
/* ISR продолжает менять измерения во время сбора param. CRC
* относится к собранному массиву, а не атомарному снимку всей платы.
* Не вызывать сохранение из критического пути дискретов.
/* ISR продолжает менять измерения во время сбора param. CRC
* относится к собранному массиву, а не атомарному снимку всей платы.
* Не вызывать сохранение из критического пути дискретов.
*/
void Save_params()
{
@@ -194,8 +191,8 @@ void Save_params()
/***************************************************************/
/***************************************************************/
/* Передача данных по протоколу ModBus - команда 3
Чтение ачеек данных */
/* Передача данных по протоколу ModBus - команда 3
Чтение ачеек данных */
/***************************************************************/
/***************************************************************/
@@ -215,63 +212,40 @@ void ReceiveCommandModbus3(RS_DATA *rs_arr)
{
unsigned int crc, Address_MB, Length_MB, i, value, saved = 0;
// получили начальный адрес чтениа
// получили начальный адрес чтениа
Address_MB = (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3];
// получили количество слов данных
// получили количество слов данных
Length_MB = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
if (Address_MB >= BALSAM167_EEPROM_RTU_BASE) { /* Выделяет только верхнее окно EEPROM; старые регистры, логгер и паспорт обрабатываются прежним кодом ниже. */
if (!balsam167_eeprom_block_valid_profile(CMD_MODBUS_3, Address_MB, Length_MB, 0U)) { /* Профиль 0 сохраняет контракт 167 для 32 КиБ: запрещает пустой блок, более 120 слов, M95 и выход за конец окна. */
modbus_exception(rs_arr, CMD_MODBUS_3, 2U); /* Возвращает Illegal Data Address с CRC до любого обращения к SPI при неверном диапазоне. */
return; /* Не допускает перехода ошибочного запроса EEPROM к backend логгера или обычным регистрам. */
} /* Валидатор проверяет конец через вычитание, поэтому переполнение 16-битного адреса не разрешает чтение. */
Seeprom_read(Address_MB - BALSAM167_EEPROM_RTU_BASE, &rs_arr->buffer[3], Length_MB * 2U); /* Как в 167, SPI получает адрес в 16-битных словах и длину в октетах; IRQ остаются разрешены для автомата Timer2. */
for (i = Length_MB; i > 0U; --i) { /* Разворачивает прочитанные слова с конца в том же UART-буфере без нового массива и перезаписи ещё не обработанных слов. */
value = rs_arr->buffer[i + 2U]; /* Временное слово сохраняет оба SPI-октета до расширения одного элемента C28x в два октета RTU. */
rs_arr->buffer[i * 2U + 2U] = value & 0xFFU; /* Записывает младший октет слова после старшего, как требует физический дамп EEPROM в SETGUI. */
rs_arr->buffer[i * 2U + 1U] = (value >> 8) & 0xFFU; /* Маска 0xFF выделяет восемь бит линии из 16-битного unsigned int C28x. */
} /* Обратный обход корректен и для одного слова, и для максимального блока из 120 слов. */
rs_arr->buffer[0] = CNTRL_ADDR; /* Отвечает собственным адресом платы, сохраняя поддержку общего адреса 16 существующим приёмником. */
rs_arr->buffer[1] = CMD_MODBUS_3; /* Возвращает стандартную команду FC03 без введения отдельной команды BIOS или записи памяти. */
rs_arr->buffer[2] = Length_MB * 2U; /* Число полезных октетов находится в диапазоне 2..240 и помещается в одном октете RTU. */
crc = get_crc_16(0xFFFFU, rs_arr->buffer, Length_MB * 2U + 3U); /* Проверяет адрес, команду, длину и весь блок тем же Modbus CRC16, что остальные ответы прошивки. */
rs_arr->buffer[Length_MB * 2U + 3U] = LOBYTE(crc); /* Передаёт младший октет CRC первым в соответствии с Modbus RTU. */
rs_arr->buffer[Length_MB * 2U + 4U] = HIBYTE(crc); /* Завершает кадр старшим октетом CRC без устаревшего нулевого хвоста. */
rs_arr->flag_TIMEOUT_to_Send = true; /* Использует штатное управление таймаутом UART при отправке готового ответа из main. */
RS_Send(rs_arr, rs_arr->buffer, Length_MB * 2U + 5U); /* Отправляет ровно 5 служебных октетов плюс данные, максимум 245 элементов из существующего буфера на 400. */
return; /* Завершает только новый маршрут EEPROM, сохраняя прежние форматы остальных ответов FC03. */
} /* Новый маршрут не меняет PEEK, запись EEPROM, адреса параметров или драйвер SPI. */
if (!Length_MB || Length_MB > 125 ||
!((Address_MB < ANSWER_LEN && Length_MB <= ANSWER_LEN-Address_MB) ||
(Address_MB >= UMP_LOG_BASE && Address_MB <= UMP_LOG_BASE+15 &&
Length_MB <= UMP_LOG_BASE+16-Address_MB) ||
(Address_MB >= UMP_LOG_DATA && Address_MB < UMP_LOG_DATA+120 &&
Length_MB <= UMP_LOG_DATA+120-Address_MB) ||
(Address_MB == UMP_LOG_LIVE && Length_MB == 124) || /* Сохраняет точное прежнее 124-словное онлайн-окно логгера. */
firmware_info_range(Address_MB, Length_MB))) { /* Дополнительно разрешает только полное или частичное чтение двенадцати регистров паспорта. */
(Address_MB == UMP_LOG_LIVE && Length_MB == 124))) {
modbus_exception(rs_arr, CMD_MODBUS_3, 2); return;
}
/////////////////////////////////////////////////
// Отсылка
/* Посчитали контрольную сумму перед самой посылкой */
// Отсылка
/* Посчитали контрольную сумму перед самой посылкой */
rs_arr->buffer[0] = CNTRL_ADDR;
rs_arr->buffer[1] = CMD_MODBUS_3;
rs_arr->buffer[2] = Length_MB*2;
/* Защищаем целостность одного снимка/блока; передача UART идёт уже
* с разрешёнными прерываниями и не задерживает измерение/управление. */
if (Address_MB >= UMP_LOG_BASE && Address_MB < FIRMWARE_INFO_ADDRESS) saved = __disable_interrupts(); /* Неизменяемый паспорт не требует блокировки IRQ; целостность прежних снимков логгера сохраняется. */
/* Защищаем целостность одного снимка/блока; передача UART идёт уже
* с разрешёнными прерываниями и не задерживает измерение/управление. */
if (Address_MB >= UMP_LOG_BASE) saved = __disable_interrupts();
for (i=0;i<Length_MB;i++)
{
if (Address_MB >= FIRMWARE_INFO_ADDRESS) value = firmware_info_static_read(Address_MB - FIRMWARE_INFO_ADDRESS + i); /* После проверки диапазона читает только соответствующее слово статического паспорта, без обращения к логгеру. */
else value = Address_MB >= UMP_LOG_BASE ? ump_log_read(Address_MB+i) : (unsigned int)modbus[Address_MB+i]; /* Сохраняет прежнюю маршрутизацию и беззнаковое представление 16-битных слов технологической карты и логгера v4. */
value = Address_MB >= UMP_LOG_BASE ? ump_log_read(Address_MB+i) : modbus[Address_MB+i];
rs_arr->buffer[3+i*2] = (value >> 8) & 0xFF;
rs_arr->buffer[3+i*2+1] = value & 0xFF;
}
if (Address_MB >= UMP_LOG_BASE && Address_MB < FIRMWARE_INFO_ADDRESS) __restore_interrupts(saved); /* Возвращает сохранённую маску только для действительно защищённого снимка логгера. */
if (Address_MB >= UMP_LOG_BASE) __restore_interrupts(saved);
crc = 0xffff;
crc = get_crc_16(crc, rs_arr->buffer, Length_MB*2+3);
@@ -284,9 +258,9 @@ void ReceiveCommandModbus3(RS_DATA *rs_arr)
rs_arr->buffer[Length_MB*2+8] = 0;
rs_arr->flag_TIMEOUT_to_Send=true;
/* Расширение логгера отвечает точным кадром Modbus без хвостовых
* нулей: они иначе попали бы в начало ответа следующего блока GUI.
* Исторический формат технологических регистров оставлен прежним. */
/* Расширение логгера отвечает точным кадром Modbus без хвостовых
* нулей: они иначе попали бы в начало ответа следующего блока GUI.
* Исторический формат технологических регистров оставлен прежним. */
RS_Send(rs_arr, rs_arr->buffer, Length_MB*2+(Address_MB >= UMP_LOG_BASE ? 5 : 8));
return;
@@ -297,27 +271,26 @@ void ReceiveCommandModbus6(RS_DATA *rs_arr)
unsigned int Address_MB, Data_MB, i;
/////////////////////////////////////////////////
// Отсылка
/* Отправлаем назад то же самое */
// Отсылка
/* Отправлаем назад то же самое */
for (i=0;i<8;i++)
rs_arr->buffer[i] = rs_arr->RS_Header[i];
// получили начальный адрес записи
// получили начальный адрес записи
Address_MB = (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3];
// получили слово данных
// получили слово данных
Data_MB = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
if (Address_MB >= FIRMWARE_INFO_ADDRESS && Address_MB < FIRMWARE_INFO_END) { modbus_exception(rs_arr, CMD_MODBUS_6, 2); return; } /* Как в контракте 167, запись в паспорт запрещена с Illegal Data Address и не достигает параметров или логгера. */
if (Address_MB >= UMP_LOG_BASE) {
if (!ump_log_write(Address_MB, Data_MB)) {
modbus_exception(rs_arr, CMD_MODBUS_6, 3); return;
}
} else if (Address_MB < ANSWER_LEN) {
/* RS и CAN используют одну историю фронтов. Чередование каналов
* не создаёт двух независимых повторных запусков. */
/* RS и CAN используют одну историю фронтов. Чередование каналов
* не создаёт двух независимых повторных запусков. */
if (Address_MB == 127) {
UMP_commands_write(Data_MB);
ump_log_rs();

View File

@@ -51,10 +51,10 @@ void get_Mode()
{
EALLOW;
GpioCtrlRegs.GPAMUX1.all &= 0xFF000000; // 00—11
GpioCtrlRegs.GPAMUX2.all &= 0xFF00003F; // 19—27
GpioCtrlRegs.GPBMUX1.all &= 0xFFFFFFC0; // 32—34
GpioCtrlRegs.GPBMUX2.all &= 0x000FF000; // 48—53, 58—63
GpioCtrlRegs.GPAMUX1.all &= 0xFF000000; // Обнуляет двухбитовые поля MUX для GPIO0–11 по карте регистров TMS320F28335, выбирая обычный GPIO и сохраняя назначение GPIO12–15.
GpioCtrlRegs.GPAMUX2.all &= 0xFF00003F; // Выбирает обычный GPIO для выводов 19–27: маска сохраняет поля GPIO16–18 и GPIO28–31 в двухбитовой таблице MUX.
GpioCtrlRegs.GPBMUX1.all &= 0xFFFFFFC0; // Обнуляет младшие шесть битов MUX, переводя GPIO32–34 в режим обычных входов или выходов без изменения остальных выводов банка.
GpioCtrlRegs.GPBMUX2.all &= 0x000FF000; // Сохраняет поля GPIO54–57 и переводит GPIO48–53 и GPIO58–63 в обычный GPIO согласно двухбитовой раскладке GPBMUX2.
GpioCtrlRegs.GPADIR.bit.GPIO20 = 0;
GpioCtrlRegs.GPADIR.bit.GPIO21 = 0;
@@ -205,7 +205,7 @@ void get_Buttons()
if(Desk==dsk_EPLT)
{
/* Фильтрует TESTLAMP на GPIO26 накопителем INcount0 с порогом 1000 опросов главного цикла; прежний bTestLamp удерживает режим теста при кратких провалах входа до подтверждения нового состояния фильтром. */
/* Сохраняет TESTLAMP до достижения порога фильтра: 1000 означает число опросов главного цикла, а bTestLamp удерживает прежний режим при кратком провале GPIO26, чтобы рабочие маски не прерывали тест индикации. */
bTestLamp = er_anal(GpioDataRegs.GPADAT.bit.GPIO26, &INcount0, 1000, bTestLamp);
if(GpioDataRegs.GPADAT.bit.GPIO27) butt +=0x0001;
@@ -224,7 +224,7 @@ void get_Buttons()
if(GpioDataRegs.GPADAT.bit.GPIO5 ) butt +=0x0800;
if(GpioDataRegs.GPBDAT.bit.GPIO48) butt +=0x1000;
if(GpioDataRegs.GPADAT.bit.GPIO10) butt +=0x2000; // Секретный вход
if(GpioDataRegs.GPADAT.bit.GPIO10) butt +=0x2000; // Публикует активный секретный вход GPIO10 в бите 13 слова Inputs; маска 0x2000 задаёт назначенную ему позицию протокола.
Inputs.all = butt;
}

View File

@@ -1,7 +1,7 @@
/* Логика пульта и индикации. cpu_timer1_isr_PULT выбирается
* только для dsk_EPLT; для нагрузки работает обработчик из measure.c.
* Lonely относится к активности обмена, не к задержке пуска СИФУ.
* Не смешивать функции двух режимов при изменении общего таймера.
/* Логика пульта и индикации. cpu_timer1_isr_PULT выбирается
* только для dsk_EPLT; для нагрузки работает обработчик из measure.c.
* Lonely относится к активности обмена, не к задержке пуска СИФУ.
* Не смешивать функции двух режимов при изменении общего таймера.
*/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
@@ -46,7 +46,8 @@ interrupt void cpu_timer1_isr_PULT(void)
EALLOW;
CpuTimer1.InterruptCount++;
IER |= MINT13; // Set "global" priority
/* Оставляет только ранее разрешённые источники по таблице приоритетов TI MINT13 и сбрасывает M_INT13 — бит 12 регистра IER, запрещая повторный Timer1 и перемешивание стробов DATA/CLOCK; исходную маску восстановит IRET. */
IER &= (MINT13 & ~M_INT13);
EINT;
EDIS; // This is needed to disable write to EALLOW protected registers
@@ -60,8 +61,8 @@ interrupt void cpu_timer1_isr_PULT(void)
toggle_ONLINE();
toggle_RES_OUT_1(); // типа это готовность
toggle_RES_OUT_2(); // на всакий
toggle_RES_OUT_1(); // Переключает резервный выход RES_OUT_1 в каждом такте пульта, сохраняя существующую выдачу признака активности обработчика.
toggle_RES_OUT_2(); // Переключает второй резервный выход RES_OUT_2 синхронно с тактом пульта, сохраняя существующее поведение этого режима платы.
if(++Lonely > Alone) Lonely = Alone;

View File

@@ -2,6 +2,4 @@
<?ccsproject version="1.0"?>
<projectOptions>
<deviceFamily value="C2000"/>
<connection value="common/targetdb/connections/SAU510USBIsoPlusV2_Connection.xml"/>
<isTargetManual value="false"/>
</projectOptions>

File diff suppressed because one or more lines are too long

View File

@@ -25,21 +25,6 @@
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures>
<linkedResources>
<link> <!-- Подключает папку заголовков приложения в дерево проекта CCS. -->
<name>Source/Internal/Include</name> <!-- Показывает заголовки рядом с исходниками приложения. -->
<type>2</type> <!-- Тип 2 обозначает связанную папку в формате проекта Eclipse/CCS. -->
<locationURI>SRC_ROOT1/Source/Internal/Include</locationURI> <!-- Находит существующие заголовки относительно корня репозитория. -->
</link> <!-- Завершает описание ссылки без копирования файлов в проект. -->
<link> <!-- Подключает общие заголовки библиотеки TI для TMS320F28335. -->
<name>Source/External/v120/DSP2833x_common/include</name> <!-- Показывает общие заголовки TI в соответствующей ветке дерева CCS. -->
<type>2</type> <!-- Тип 2 позволяет отобразить всё содержимое связанной папки. -->
<locationURI>SRC_ROOT1/Source/External/v120/DSP2833x_common/include</locationURI> <!-- Использует ту же папку, которая уже указана в путях компилятора. -->
</link> <!-- Завершает ссылку на общие заголовки TI. -->
<link> <!-- Подключает заголовки регистров и периферии процессора TI. -->
<name>Source/External/v120/DSP2833x_headers/include</name> <!-- Показывает аппаратные заголовки в дереве исходников CCS. -->
<type>2</type> <!-- Тип 2 задаёт ссылку на папку, а не на отдельный файл. -->
<locationURI>SRC_ROOT1/Source/External/v120/DSP2833x_headers/include</locationURI> <!-- Ссылается на существующую папку аппаратных заголовков внутри репозитория. -->
</link> <!-- Завершает ссылку на аппаратные заголовки TI. -->
<link>
<name>F28335.cmd</name>
<type>1</type>

View File

@@ -1,8 +1,4 @@
eclipse.preferences.version=1
# Показывает пояснения периода CAN и обработки параметров в сохранённой кодировке UTF-8.
encoding//Source/Internal/message.c=UTF-8
# Показывает русские пояснения выбора исполнения и порядка индикаторов в кодировке UTF-8.
encoding//Source/Internal/Include/package.h=UTF-8
encoding//Debug/Libraries/subdir_rules.mk=UTF-8
encoding//Debug/Libraries/subdir_vars.mk=UTF-8
encoding//Debug/Source/External/v120/DSP2833x_common/source/subdir_rules.mk=UTF-8
@@ -108,8 +104,3 @@ encoding//Debug/optimizerAssistant/opt_for_speed__5/subdir_vars.mk=UTF-8
encoding//Debug/sources.mk=UTF-8
encoding//Debug/subdir_rules.mk=UTF-8
encoding//Debug/subdir_vars.mk=UTF-8
encoding//Source/Internal/Include/log_to_mem.h=UTF-8
encoding//Source/Internal/Include/ump_can_protocol.h=UTF-8
encoding//Source/Internal/log_to_mem.c=UTF-8
encoding//Source/Internal/peripher.c=UTF-8
encoding/<project>=UTF-8

View File

@@ -16,47 +16,14 @@
Файл `28335_RAM_lnk.cmd`, как и в 167, исключён из сборки.
Результаты сборки находятся в `166/Bin`: `UKSSTMS320F28335.out`,
`UKSSTMS320F28335.hex` (Intel HEX) и `UKSSTMS320F28335_<build_id>.bin`.
Рядом с BIN создаются `<имя>.bin.manifest.json` и `<имя>.bin.sha256`.
Build ID содержит восемь символов: восемь цифр ревизии либо семь цифр и `+`
при изменённых исходниках или общей библиотеке. При недоступном Git общий
контракт 167 использует `NOGIT000`; для выпуска следует проверить ревизии
`source` и `templates` в манифесте. Время паспорта хранится в UTC.
Prebuild `prepare_identity.py` использует тот же генератор firmware-info,
что 167, но product равен `Balsam-166-PM35`. Единственный источник версии —
`firmware_version.h`; первый выпуск с паспортом имеет версию **1.0.0**.
Обязательный `generated/firmware_identity.h` создаётся до компиляции.
Postbuild `build_identity.py` использует hex2000 из выбранного компилятора
CCS, общий манифест и SHA-256, затем независимо проверяет наличие ровно
одного соответствующего 12-словного паспорта в SCI8. Старый OUT без паспорта
или BIN с чужими полями не публикуется. Превышение границы параметров EEPROM
**32508 байт** также останавливает выпуск. Архивы сохраняются локально в
`UKSSTMS320F28335/Debug/firmware`; записи в репозиторий GUI нет.
`build_bin.bat` сохраняет ручную точку входа для уже скомпилированного OUT,
но теперь также требует его совпадения с паспортом соответствующего prebuild.
Простая конверсия старой прошивки не может создать ей достоверную версию.
Карта линковки остаётся в корне 166, карта конверсии — в `166/Bin`.
## Проверка прошивки в SetGUI
Из 167 точечно перенесён контракт firmware-info v1: FC03, адрес **0x1200**,
**12 регистров**, версия, дата UTC и Build ID. Чтение доступно также через
существующий служебный CAN; запись в паспорт запрещена. Технологическая
карта, логгер v4 с записью 12 слов, управление и CAN ID не меняются.
В GUI выберите модель **«Бальзам-166 · ПМ35»**, новый BIN и оставьте рядом
его `.bin.manifest.json`. После записи EEPROM перезапустите плату и нажмите
чтение версии: продолжающая работать старая прошивка ещё не имеет окна
0x1200 и отвечает `Modbus 2`. Надпись CRC OK при записи подтверждает запись
EEPROM, а версия начнёт читаться после запуска нового образа. Сравнение
версий и SHA-256 манифеста не является измерением SHA всей памяти платы.
`python tests/firmware_identity/run_test.py` проверяет реальные обработчики
RS/CAN, все слова паспорта, CRC, частичные чтения, границы, запрет записи,
сохранность старой карты и логгера. Отдельно проверяются устаревшие и
повреждённые SCI8, предел EEPROM и действующий анализатор манифеста SetGUI.
`UKSSTMS320F28335.hex` (Intel HEX) и `UKSSTMS320F28335_<commit>.bin`,
где `<commit>` — короткий хеш текущего коммита Git (не менее 8 символов).
BIN создаётся скриптом `build_bin.bat`; Git должен быть доступен в `PATH`
процесса CCS. Если определить коммит не удалось, сборка завершается ошибкой.
При наличии незакоммиченных правок хеш обозначает HEAD и не описывает эти правки.
Post-build использует имеющийся `166/Bin/hex2000.exe` с параметрами
`-boot -sci8`, как в 167. Карта линковки находится в корне 166, карта
преобразования загрузочного образа — в `166/Bin`.
Проверено: импорт и полная сборка **Debug** в CCS **12.7.1** с компилятором
**22.6.1.LTS**, включая генерацию HEX/BIN, завершились без ошибок.
@@ -64,34 +31,5 @@ RS/CAN, все слова паспорта, CRC, частичные чтения
(преобразование знака, выход результата за диапазон и отсутствие перевода
строки в конце файла). Проверка на устройстве не выполнялась.
Перенос паспорта дополнительно проверен полной пересборкой и линковкой
TI C2000 **22.6.0.LTS** и host-тестами под GCC с оптимизацией O3. Проверенный
SCI8 BIN занимает **30248 байт**, запас до параметров EEPROM — **2260 байт**.
Совпадение встроенного паспорта, манифеста и результата штатного анализатора
SetGUI проверено программно; обмен новой прошивки с физической платой
требует её установки и перезапуска.
Конфигурация **Release** перенесена из 167 без изменений и не проверялась;
для сборки прошивки используйте **Debug**.
## Телеметрия CAN: heartbeat, изменения и повтор 1,3 с
Для всех Mode технологические пакеты выбранных масками регистров R0–R127
передаются при первом снимке, изменении любого из трёх слов или через 1300 мс
после последнего успешного CAN ACK этого пакета. Неудачная попытка не обновляет
подтверждённый снимок. Конечные пакеты карты дополняются нулями за R127.
Сканирование изменений идёт по Timer1 с шагом 20 мс; готовая очередь выдаётся
по мере освобождения mailbox, без ожидания нового периода между её кадрами.
Фактическую отправку могут задержать занятая шина, ошибки и работа main.
Heartbeat отправляется независимо раз в секунду: адрес `0x0100`, затем три
16-битных слова — счётчик секунд, Mode, Desk. После задержки main отправляется
последнее значение, без очереди пропущенных секундных кадров.
Ноль в `Cancount[0]` или `Cancount[1]` по-прежнему отключает данные соответствующей
маски. Любое ненулевое значение включает новый режим изменения/1300 мс;
прежний числовой делитель периода больше не используется. Heartbeat остаётся
включённым при отключённых потоках данных. Параметры EEPROM не переносятся.
После ошибки TX новая попытка разрешается через секунду без ожидания в цикле.
Проверка без CAN-адаптера: `python tests/can_telemetry/run_test.py`.

View File

@@ -1,67 +0,0 @@
"""Выпускает BIN 166 с проверяемым паспортом и SHA-256 по той же схеме, что 167.""" # Генерация версии выполняется отдельно до компиляции.
import argparse # Разбирает пути текущей конфигурации CCS и компилятора TI.
from datetime import datetime # Преобразует время UTC паспорта в те же шесть компонентов, что firmware-info v1.
import json # Читает именно паспорт завершившейся компиляции.
from pathlib import Path # Привязывает настройки и результаты к текущему проекту.
import shutil # Находит компилятор и копирует только полностью проверенный выпуск.
import struct # Проверяет 16-битные слова загрузочного SCI8 без исполнения кода прошивки.
import sys # Подключает общий postbuild вместо копии конвертера из 167.
import tempfile # Не публикует результаты конверсии, пока встроенный паспорт не прошёл проверку.
ROOT = Path(__file__).resolve().parents[1] # Корень 166 содержит существующие Source и Bin.
PROJECT = Path(__file__).resolve().parent # Конфигурации Debug/Release и заголовок версии находятся рядом со сценарием.
TEMPLATES = ROOT.parent / "newProject/templates" # Использует ту же общую библиотеку firmware-info, что проект 167.
def verify_embedded(image, metadata): # Не позволяет выдать новый манифест за паспорт старого OUT без встроенной версии.
data = image.read_bytes() # Проверяет точные октеты, которые будут отправлены в плату.
if data[:2] != b"\xaa\x08" or not 24 <= len(data) <= 32508: # SCI8 имеет ключ 0x08AA; граница параметров 166 равна (0x3FFF - 129) * 2 = 32508 байт.
raise ValueError("SCI8 is invalid or overlaps the 166 parameter area at byte 32508") # Не допускает повреждения существующих параметров EEPROM.
stamp = datetime.fromisoformat(metadata["built_at_utc"]) # Использует исходное время prebuild без подмены временем конверсии BIN.
build_id = metadata["build_id"] # Ровно восемь ASCII-символов совпадают с четырьмя регистрами firmware-info v1.
words = [1, *metadata["version"], stamp.year, stamp.month * 256 + stamp.day, stamp.hour * 256 + stamp.minute, stamp.second] # Восстанавливает первые восемь регистров независимо от C-упаковщика.
words += [ord(build_id[i]) * 256 + ord(build_id[i + 1]) for i in range(0, 8, 2)] # Восстанавливает четыре слова Build ID в сетевом порядке символов.
expected = struct.pack("<12H", *words) # Загрузочная таблица SCI8 хранит 16-битные слова C28x младшим октетом вперёд.
offset, found = 22, 0 # Первые 22 октета заняты ключом, настройками и аппаратной точкой входа.
while offset + 2 <= len(data): # Каждый шаг потребляет заголовок либо завершает конечный файл; ожидания аппаратуры нет.
count, = struct.unpack_from("<H", data, offset) # Размер очередной секции задан в 16-битных словах C28x.
offset += 2 # Переходит к адресу секции либо концу таблицы при нулевом размере.
if not count: break # Нулевая секция завершает SCI8 и не содержит адреса назначения.
end = offset + 4 + count * 2 # Четыре октета адреса не входят в проверяемую полезную нагрузку секции.
if end > len(data): raise ValueError("Truncated SCI8 section") # Останавливает выпуск при выходе секции за фактический размер файла.
payload = data[offset + 4:end] # Рассматривает загружаемые слова, а не случайное совпадение в заголовке.
found += sum(payload[i:i + 24] == expected for i in range(0, len(payload) - 23, 2)) # Паспорт должен присутствовать один раз на границе слова C28x.
offset = end # Переходит к следующей непересекающейся секции таблицы.
if offset != len(data) or count or found != 1: # Запрещает хвосты, отсутствующий терминатор и манифест от другой компиляции.
raise ValueError(f"SCI8 does not contain exactly one matching prebuild identity: {found}") # Требует полноценную пересборку вместо фиктивного подписания старого BIN.
def find_compiler(): # Сохраняет возможность ручного запуска build_bin.bat без жёсткой привязки к одной версии CCS.
executable = shutil.which("cl2000") or shutil.which("cl2000.exe") # Предпочитает компилятор среды текущего проекта, а не старый hex2000 рядом с BIN.
if executable: return Path(executable).resolve().parents[1] # Возвращает корень toolchain с каталогом bin/hex2000.exe.
installed = sorted(Path("C:/ti").glob("ccs*/ccs/tools/compiler/ti-cgt-c2000_*")) # Для установленного CCS использует только штатные каталоги TI.
installed = [path for path in installed if (path / "bin/hex2000.exe").is_file()] # Не принимает неполную установку за пригодный конвертер.
if not installed: raise FileNotFoundError("Pass --compiler with the C2000 toolchain root") # Явно останавливает ручной запуск без инструментов.
return installed[-1] # Выбирает последнюю найденную установленную цепочку только при отсутствии заданной CCS версии.
def build(compiler, build_dir, artifact): # Выполняет общий postbuild и локальную публикацию после проверки встроенной версии.
sys.path.insert(0, str(TEMPLATES / "c/firmware-info/tools")) # Открывает общие identity.py и c2000_postbuild.py из той же зависимости, что 167.
from c2000_postbuild import postbuild # Не копирует формат манифеста, архивирование и вычисление SHA-256.
metadata_path = build_dir / "generated/firmware_identity.json" # Отсутствующий prebuild должен остановить выпуск вместо использования произвольной версии.
metadata = json.loads(metadata_path.read_text(encoding="utf-8")) # Читает JSON, по которому компилятор получил firmware_identity.h.
if metadata.get("product") != "Balsam-166-PM35" or metadata.get("target") != "TMS320F28335": raise ValueError("Run the 166 prebuild before publishing this image") # Старый generated от ветки 167 не должен выдавать её product за паспорт 166.
output = build_dir / "firmware" # Сохраняет прежнюю для 167 структуру локального каталога выпусков.
with tempfile.TemporaryDirectory(prefix=".identity-release-", dir=build_dir) as directory: # Непроверенные BIN и manifest существуют только во временном каталоге текущего вызова.
stage = Path(directory); staged_image = postbuild(compiler, build_dir, artifact, metadata_path, stage) # Общий код создаёт HEX, BIN, SHA-256 и архив, пока не доступные как готовый выпуск.
verify_embedded(staged_image, metadata) # Не переносит в firmware или Bin результаты от старого OUT либо другой компиляции.
output.mkdir(parents=True, exist_ok=True) # Создаёт каталог публикации только после успешной проверки.
for item in stage.iterdir(): # Сохраняет только результаты этого вызова, не сканируя старые выпуски.
if item.is_dir(): shutil.copytree(item, output / item.name, dirs_exist_ok=True) # Публикует полный архив проверенной сборки.
else: shutil.copy2(item, output / item.name) # Переносит плоские HEX/BIN и контрольные файлы без подмены других исходников.
image = output / staged_image.name # После удаления временного каталога возвращает постоянный путь проверенного BIN.
for file in (image, image.with_suffix(".bin.manifest.json"), image.with_suffix(".bin.sha256"), image.parent / (image.stem.rsplit("_", 1)[0] + ".hex")): # Сохраняет рядом проверенный BIN, его контрольные файлы и прежний Intel HEX для CCS.
shutil.copy2(file, ROOT / "Bin" / file.name) # Основной каталог получает только результат успешной текущей сборки.
shutil.copy2(image.parent / (image.stem.rsplit("_", 1)[0] + ".hex.map"), ROOT / "Bin/UKSSTMS320F28335.map") # Согласовывает прежнюю основную карту конверсии с новым HEX и OUT.
print(ROOT / "Bin" / image.name) # Показывает конкретный BIN, который нужно выбрать в SetGUI вместе с лежащим рядом манифестом.
return image # Позволяет тестам проверить результат без поиска случайного старого файла.
if __name__ == "__main__": # Импорт проверки SCI8 не выполняет сборку или публикацию файлов.
parser = argparse.ArgumentParser(description=__doc__) # Описывает контракт запуска из CCS и build_bin.bat.
parser.add_argument("--compiler", type=Path) # CCS передаёт CG_TOOL_ROOT; ручной вызов может использовать обнаружение установленного компилятора.
parser.add_argument("--build-dir", type=Path, default=PROJECT / "Debug") # Изолирует паспорт и архив выбранной конфигурации.
parser.add_argument("--artifact", default=str(ROOT / "Bin/UKSSTMS320F28335")) # Общий postbuild ожидает полный путь OUT без расширения.
args = parser.parse_args() # Проверяет параметры до выполнения конверсии.
build((args.compiler or find_compiler()).resolve(), args.build_dir.resolve(), args.artifact) # Публикует только образ, соответствующий своему встроенному паспорту.

View File

@@ -1,4 +0,0 @@
{
"product": "Balsam-166-PM35",
"target": "TMS320F28335"
}

View File

@@ -1,6 +0,0 @@
#ifndef BALSAM_FIRMWARE_VERSION_H /* Защищает единственный источник версии продукта 166 от повторного включения. */
#define BALSAM_FIRMWARE_VERSION_H /* Отделяет номер версии приложения от версии логгера и контракта паспорта. */
#define FIRMWARE_VERSION_MAJOR 1U /* Первая версия 166 с читаемым паспортом; это версия продукта, а не заимствованный номер прошивки 167. */
#define FIRMWARE_VERSION_MINOR 0U /* Нулевая минорная версия начинает самостоятельную линию выпусков Balsam-166-PM35. */
#define FIRMWARE_VERSION_PATCH 0U /* Нулевой номер исправления меняется при следующем выпуске продукта. */
#endif /* Генератор читает эти три определения перед компиляцией и помещает их в паспорт BIN. */

View File

@@ -1,8 +1,33 @@
@rem Совместимая точка входа старых make-файлов Debug после подключения паспорта прошивки.
@echo off
rem Сохраняет локальность настроек процесса конвертации.
setlocal
rem Сценарий сам находит Debug и проверяет его generated/firmware_identity.json; старые служебные аргументы CCS здесь не нужны.
python "%~dp0build_identity.py"
rem Возвращает ошибку при старом OUT, отсутствующем паспорте либо превышении области параметров.
for %%I in ("%~dp0..") do set "SRC_ROOT=%%~fI"
:process_arg
if "%1"=="" goto end_process_arg
set name=%1
set value=
:process_arg_value
if NOT "%value%"=="" set value=%value% %2
if "%value%"=="" set value=%2
shift
if "%2"=="!" goto set_arg
if "%2"=="" goto set_arg
goto process_arg_value
:set_arg
set %name%=%value%
shift
shift
goto process_arg
:end_process_arg
pushd "%SRC_ROOT%\Bin" || exit /b 1
hex2000.exe UKSSTMS320F28335.out -boot -sci8 -map UKSSTMS320F28335.map -o UKSSTMS320F28335.hex -i
set "BUILD_RESULT=%errorlevel%"
popd
if not "%BUILD_RESULT%"=="0" exit /b %BUILD_RESULT%
call "%SRC_ROOT%\build_bin.bat"
exit /b %errorlevel%

View File

@@ -1,35 +0,0 @@
"""Подготавливает паспорт 166 тем же генератором firmware-info, что используется в 167.""" # Версия, время UTC и ревизии фиксируются до компиляции.
import argparse # Разбирает одинаковые с проектом 167 пути prebuild.
import importlib.util # Подключает общую реализацию без копирования генератора в прошивку.
import json # Объединяет идентификаторы продукта с версией из C-заголовка.
from pathlib import Path # Нормализует пути независимо от рабочего каталога CCS.
import re # Проверяет единственное допустимое целое определение каждого компонента версии.
import tempfile # Хранит объединённые настройки только на время вызова общего генератора.
def read_version(header): # Сохраняет правила исходного адаптера 167 и ограничения firmware-info v1.
text = re.sub(r"/\*.*?\*/|//[^\n]*", "", header.read_text(encoding="utf-8-sig"), flags=re.S) # Не принимает комментарии или примеры за действующую версию.
result = [] # Накапливает major, minor и patch в порядке регистров контракта.
for part, limit in (("MAJOR", 255), ("MINOR", 255), ("PATCH", 999)): # Два первых компонента ограничены октетом, исправление допускает 0..999.
name = "FIRMWARE_VERSION_" + part # Использует тот же интерфейс заголовка версии, что 167.
definitions = re.findall(r"^\s*#\s*define\s+" + name + r"\b([^\n]*)", text, re.M) # Требует единственного определения без условных альтернатив.
if len(definitions) != 1 or not re.fullmatch(r"(?:0|[1-9][0-9]*)[uU]?", definitions[0].strip()): # Не допускает выражений, шестнадцатеричных литералов и неоднозначной версии.
raise ValueError(f"{header}: define {name} exactly once as a decimal integer") # Останавливает сборку вместо выпуска ложного паспорта.
value = int(definitions[0].strip().rstrip("uU")) # Суффикс C не является частью десятичного числа Python.
if value > limit: # Проверяет границы до записи какого-либо заголовка сборки.
raise ValueError(f"{name} must be in 0..{limit}") # Сохраняет ограничения отображения и Modbus-контракта 167.
result.append(value) # Записывает проверенную компоненту в правильное поле паспорта.
return result # Номер версии формируется только из firmware_version.h.
def prepare(project, templates, output): # Генерирует заголовок и JSON для одной конкретной конфигурации CCS.
settings = json.loads((project / "firmware_identity.json").read_text(encoding="utf-8-sig")) # Сохраняет собственный product 166 и тот же target TMS320F28335.
settings["version"] = read_version(project / "firmware_version.h") # Не дублирует SemVer в двух редактируемых файлах.
spec = importlib.util.spec_from_file_location("shared_firmware_identity", templates / "c/firmware-info/tools/identity.py") # Берёт проверенную общую реализацию из той же зависимости, что 167.
identity = importlib.util.module_from_spec(spec) # Создаёт изолированный модуль генератора без изменения системного Python.
spec.loader.exec_module(identity) # Загружает формат заголовка, UTC и правила формирования Build ID.
with tempfile.TemporaryDirectory(prefix="balsam166-identity-") as directory: # Удаляет только временные объединённые настройки этого вызова.
config = Path(directory) / "settings.json" # Настройки не записываются в исходники и не попадают в историю Git.
config.write_text(json.dumps(settings), encoding="utf-8") # Передаёт общему генератору проверенную версию и product.
return identity.prepare(project, templates, config, output) # Создаёт обязательные generated/firmware_identity.h и JSON до компиляции.
if __name__ == "__main__": # Импорт из теста не запускает изменение файлов сборки.
parser = argparse.ArgumentParser(description=__doc__) # Объясняет назначение prebuild при ручном вызове.
for name in ("project", "templates", "output"): parser.add_argument("--" + name, type=Path, required=True) # Совместим с параметрами prebuild исходного проекта 167.
args = parser.parse_args() # Проверяет обязательные каталоги перед генерацией.
print(json.dumps(prepare(args.project.resolve(), args.templates.resolve(), args.output.resolve()))) # Показывает фактическую идентичность текущей сборки.

View File

@@ -1,8 +1,16 @@
@rem Повторно конвертирует готовый OUT только при совпадении со встроенным паспортом текущего prebuild.
@echo off
rem Ограничивает переменные окружения этим вызовом и не меняет настройки пользователя.
setlocal
rem Общий проектный postbuild создаёт BIN, manifest, SHA-256 и проверяет слова паспорта до публикации.
python "%~dp0UKSSTMS320F28335\build_identity.py" %*
rem Передаёт вызывающему процессу фактическую ошибку проверки или конверсии.
exit /b %errorlevel%
set "SRC_ROOT=%~dp0"
set "GIT_COMMIT="
for /f "delims=" %%H in ('git -C "%SRC_ROOT%." rev-parse --short^=8 HEAD') do set "GIT_COMMIT=%%H"
if not defined GIT_COMMIT (
echo ERROR: Cannot determine the Git commit. Make Git available in PATH. 1>&2
exit /b 1
)
pushd "%SRC_ROOT%Bin" || exit /b 1
hex2000.exe UKSSTMS320F28335.out -boot -sci8 -map UKSSTMS320F28335.map -o UKSSTMS320F28335_%GIT_COMMIT%.bin -b
set "BUILD_RESULT=%errorlevel%"
popd
exit /b %BUILD_RESULT%

View File

@@ -1,107 +0,0 @@
#include <stdint.h> /* Фиксирует ширину таймера и аппаратных полей eCAN независимо от разрядности host-компилятора. */
#include <stdio.h> /* Выводит точную строку нарушенного сценария вместо молчаливого неверного результата. */
#include <string.h> /* Изолирует независимые случаи очисткой модели аппаратуры и данных. */
typedef uint16_t Uint16; typedef uint32_t Uint32; /* Повторяет 16- и 32-битные аппаратные типы TI C28x. */
#define READY_FREQ 1000U /* Один такт Timer1 равен одной миллисекунде, как в действующей прошивке 166. */
#define CANPOWSE 20U /* Прежний базовый интервал обнаружения обновлений данных составляет 20 мс. */
#define ANSWER_LEN 128 /* Как в package.h, технологическая карта содержит ровно R0–R127 без дополнительного запаса для ошибочного чтения. */
#define interrupt /* Host-модель вызывает настоящий ISR как обычную функцию без атрибутов процессора TI. */
#define EALLOW ((void)0) /* Защищённый доступ аппаратных регистров отдельно проверяется настоящим компилятором TI. */
#define EDIS ((void)0) /* Модель не подменяет логику изменения битов тайм-аута mailbox. */
#define EINT ((void)0) /* Тесты явно выбирают момент аппаратного прерывания, исключая недетерминированные потоки. */
#define DINT ((void)0) /* Сценарий проверяет восстановление маски, не симулируя планировщик ОС. */
#define M_INT9 0x0100U /* Девятая группа PIE обслуживает eCAN в production-обработчике. */
#define MINT9 0x0100U /* Сохраняет аппаратную маску CPU INT9 при обработке ошибки передачи. */
#define MG96 0x00DFU /* Маска соответствует подавлению вложенного INTx6 в старом ISR проекта. */
struct REG { Uint32 all; }; /* Даёт production-коду настоящие 32-битные поля регистров eCAN. */
struct ECAN_REGS { struct REG CANTA, CANAA, CANTRR, CANTRS, CANTOC, CANTOS; Uint32 CANTSC; }; /* Моделирует только используемый жизненный цикл штатного mailbox. */
static volatile struct ECAN_REGS ECanaRegs; /* Volatile сохраняет все наблюдаемые записи production-функций при оптимизации O3. */
static struct { struct { struct REG MDH, MDL; } MBOX0; } ECanaMboxes; /* Восемь октетов передаваемого кадра наблюдаются независимо от кэша алгоритма. */
static struct { Uint32 InterruptCount; } CpuTimer1; /* Счётчик переполняется в точности через 2^32 тактов. */
static struct { struct { Uint16 all; } PIEIER9; union { unsigned all; struct { unsigned reserved:8, ACK9:1; } bit; } PIEACK; } PieCtrlRegs; /* Сохраняет реальное положение PIEACK9 в маске подтверждения IRQ. */
static Uint16 IER; static unsigned int irq_mask; /* Позволяет проверить возврат исходного состояния прерываний после короткого снимка данных. */
static int wait, CanTimeOutErrorTR, Mode, Desk, modbus[ANSWER_LEN]; /* Использует настоящий размер массива технологических слов и наблюдаемые параметры узла. */
static unsigned int Maska[2][8]; /* Каждому потоку соответствует восемь 16-битных масок разрешённых регистров. */
#define Cancount (modbus + 0x60) /* Как в package.h, R96 и R97 определяют включение двух потоков данных. */
static unsigned int __disable_interrupts(void) { unsigned int previous = irq_mask; irq_mask = 1U; return previous; } /* Имитирует сохранение исходной маски IRQ даже при вложенном чтении Timer1. */
static void __restore_interrupts(unsigned int previous) { irq_mask = previous; } /* Выявляет ошибочное безусловное разрешение прерываний отправителем. */
static void led1_toggle(void) {} /* Индикация не влияет на подтверждение доставки или период таймера. */
#include "can_telemetry_impl.h" /* Компилирует точный production-алгоритм, а не отдельную копию реализации в тесте. */
/* TIMEOUT_ISR */
#define CHECK(condition) do { if (!(condition)) { printf("FAIL line %d: %s\n", __LINE__, #condition); return 1; } } while (0) /* Проверка остаётся активной при оптимизации и показывает нарушенное ожидание. */
static Uint32 frames_hi[2048], frames_lo[2048], frames_at[2048]; static unsigned int count; /* Журнал хранит только действительно запущенные аппаратные кадры и момент их передачи. */
static void hardware_ack(void) { ECanaRegs.CANTRS.all &= ~1UL; ECanaRegs.CANTRR.all &= ~1UL; ECanaRegs.CANTA.all = 1UL; ECanaRegs.CANAA.all = 0UL; } /* Завершает именно mailbox 0 успешным аппаратным ACK. */
static void prepare(unsigned int mode, unsigned int desk) { /* Начинает независимый сценарий, включая имитацию повторного запуска программы. */
memset((void *)&ECanaRegs, 0, sizeof(ECanaRegs)); memset(&ECanaMboxes, 0, sizeof(ECanaMboxes)); memset(&PieCtrlRegs, 0, sizeof(PieCtrlRegs)); /* Сбрасывает только модель периферии между испытаниями. */
memset(modbus, 0, sizeof(modbus)); memset(Maska, 0, sizeof(Maska)); CpuTimer1.InterruptCount = 0U; count = 0U; irq_mask = 0U; /* Устанавливает известное начальное содержимое карты и таймера. */
Mode = mode; Desk = desk; Cancount[0] = 10; Cancount[1] = 101; IER = M_INT9; PieCtrlRegs.PIEIER9.all = 0x0030U; CanTimeOutErrorTR = 0; /* Периоды 10 и 101 проверяют независимость нового повтора от старых делителей. */
CAN_telemetry_init(); /* Первый снимок после каждого запуска должен отправляться независимо от содержимого старых кэш-массивов. */
} /* Аппаратный ACK в каждом сценарии задаётся явно. */
static int pump(void) { /* Выдаёт всю уже готовую очередь с ACK между кадрами, не продвигая тестовое время. */
unsigned int limit; /* Ограничивает итерации для обнаружения зависания на пустой маске или неизменном пакете. */
for (limit = 0U; limit < 512U; ++limit) { /* Полная карта из двух потоков требует значительно меньше 512 аппаратных кадров. */
CAN_telemetry_service(); /* Обслуживает реальную очередь, heartbeat и индивидуальные периоды подтверждённых данных. */
CHECK(irq_mask == 0U); /* После любого пути отправки прерывания должны иметь первоначальное состояние. */
if (!(ECanaRegs.CANTRS.all & 1UL)) return 0; /* Отсутствие нового TX означает завершённую выдачу готовых кадров. */
CHECK(count < 2048U); /* Исключает скрытое переполнение тестового журнала. */
frames_hi[count] = ECanaMboxes.MBOX0.MDH.all; frames_lo[count] = ECanaMboxes.MBOX0.MDL.all; frames_at[count++] = CpuTimer1.InterruptCount; /* Запоминает точные данные mailbox, не используя внутренний кэш production-кода как эталон. */
hardware_ack(); /* Только успешное подтверждение разрешает алгоритму подавить следующий неизменный пакет. */
} /* Бесконечное повторение выявляется как ошибка конечной модели. */
CHECK(0); /* Даже при пустой маске или неподтверждённом адресе сервис обязан возвращаться без блокировки. */
return 1; /* Сохраняет однозначный код ошибки для компилятора C. */
} /* Все вызовы pump выполняются с фиксированным временем, поэтому сравнения периодов точны. */
static unsigned int address_at(unsigned int index) { return (unsigned int)((frames_hi[index] >> 16) & 0x1FFFU); } /* Декодирует существующий маркер BALZAM независимо от внутренней реализации отправителя. */
static unsigned int count_address(unsigned int address) { unsigned int index, found = 0U; for (index = 0U; index < count; ++index) if (address_at(index) == address) ++found; return found; } /* Считает реальные кадры выбранного технологического или heartbeat-адреса. */
int main(void) { /* Проверяет требования пользователя, границы карты и ошибки аппаратного TX на настоящем коде прошивки. */
unsigned int mode, before, index; Uint32 started; /* Сценарии используют независимые ожидаемые периоды в миллисекундах. */
for (mode = 1U; mode <= 9U; ++mode) { /* Сравнение данных и повтор 1,3 с применяются ко всем аппаратным Mode, включая задатчик. */
prepare(mode, mode == 8U ? 4U : 0U); Maska[0][0] = 1U; modbus[0] = 9; modbus[1] = 12; modbus[2] = -1; CHECK(pump() == 0); /* Начальный пакет должен передаться сразу в прежнем порядке трёх слов. */
CHECK(count == 1U && frames_hi[0] == 0xE0000009UL && frames_lo[0] == 0x000CFFFFUL); /* Отрицательное 16-битное значение не портит упаковку и старший регистр. */
CpuTimer1.InterruptCount = 999U; CHECK(pump() == 0 && count == 1U); /* Неизменившиеся данные не создают лишних кадров до секундного heartbeat. */
CpuTimer1.InterruptCount = 1000U; CHECK(pump() == 0 && count == 2U && address_at(1U) == 0x100U); /* Heartbeat появляется ровно через одну секунду, независимо от обоих Cancount. */
CHECK((frames_hi[1] & 0xFFFFUL) == 1U && frames_lo[1] == ((Uint32)Mode << 16 | (unsigned int)Desk)); /* Отдельный heartbeat содержит счётчик секунд, Mode и Desk. */
CpuTimer1.InterruptCount = 1299U; CHECK(pump() == 0 && count_address(0U) == 1U); /* Технологический пакет не повторяется раньше индивидуального срока. */
CpuTimer1.InterruptCount = 1300U; CHECK(pump() == 0 && count_address(0U) == 2U); /* Неизменные данные повторяются на границе 1,3 с без искусственного изменения состояния планировщика тестом. */
CHECK(frames_at[count - 1U] == 1300U && frames_hi[count - 1U] == frames_hi[0]); /* Повторяет сами данные, а не только отдельный heartbeat. */
} /* Завершает проверку одинакового поведения всех Mode. */
prepare(5U, 0U); Maska[0][0] = 1U; Maska[1][0] = 8U; modbus[0] = 1; modbus[3] = 2; CHECK(pump() == 0 && count == 2U); /* Два потока получают отдельные пакеты и подтверждения без ожидания старых делителей. */
CpuTimer1.InterruptCount = 40U; modbus[1] = 77; CHECK(pump() == 0 && count == 3U && address_at(2U) == 0U); /* Изменение второго слова пакета отправляется до истечения 1,3 с. */
CpuTimer1.InterruptCount = 60U; modbus[2] = 88; CHECK(pump() == 0 && count == 4U && (frames_lo[3] & 0xFFFFUL) == 88U); /* Изменение третьего слова тоже обнаруживается и не подавляется кэшем первого регистра. */
CpuTimer1.InterruptCount = 1300U; CHECK(pump() == 0 && count_address(3U) == 2U && count_address(0U) == 3U); /* Независимый пакет повторяется через 1,3 с, а обновлённый пакет R0 ещё не достиг своего нового срока. */
CpuTimer1.InterruptCount = 1360U; CHECK(pump() == 0 && count_address(0U) == 4U); /* Период R0 отсчитывается заново от ACK последнего изменённого снимка. */
prepare(8U, 4U); Maska[0][1] = 1U; Maska[1][0] = 15U; ECanaRegs.CANTRS.all = 1U; ECanaMboxes.MBOX0.MDH.all = 0x12345678UL; CAN_telemetry_service(); /* Занятый внешним запросом mailbox нельзя менять или считать доставленным. */
CHECK(ECanaMboxes.MBOX0.MDH.all == 0x12345678UL && can_scan_due == 1U); /* Оба потока сохраняют готовность до физического освобождения передатчика. */
ECanaRegs.CANTRS.all = 0U; CHECK(pump() == 0 && count_address(16U) == 1U && count_address(0U) == 1U && count_address(3U) == 1U); /* После занятости не теряются R0–R2 и R3–R5 медленного потока. */
prepare(5U, 0U); Maska[0][0] = 1U; CAN_telemetry_service(); CHECK(wait && !(can_valid[0] & 1U)); /* Постановка первого пакета в TX ещё не означает успешную доставку. */
ECanaRegs.CANTA.all = 0U; ECanaRegs.CANTOS.all = 1U; ECanaRegs.CANTOC.all = 5U; CANa_reset_err(); /* Воспроизводит аппаратный timeout, пока отдельный контроль служебного mailbox тоже включён. */
CHECK(can_tx_failed && CanTimeOutErrorTR == 1 && ECanaRegs.CANTRR.all == 1U && ECanaRegs.CANTOC.all == 4U); /* ISR помечает отказ и отменяет только штатный TX без выключения служебного контроля. */
ECanaRegs.CANTOS.all &= ECanaRegs.CANTOC.all; ECanaRegs.CANTRS.all = ECanaRegs.CANTRR.all = 0U; ECanaRegs.CANAA.all = 0U; CpuTimer1.InterruptCount = 10U; CAN_telemetry_service(); /* Отмена завершена без CANAA: отсутствие этого флага не должно навсегда заблокировать очередь. */
CHECK(!wait && can_backoff && !(can_valid[0] & 1U)); /* Неудачный снимок не становится подтверждённым и остаётся кандидатом следующей попытки. */
CpuTimer1.InterruptCount = 1009U; modbus[0] = 42; CAN_telemetry_service(); CHECK(!(ECanaRegs.CANTRS.all & 1U)); /* До окончания секундной паузы после ошибки повтор не засоряет CAN. */
CpuTimer1.InterruptCount = 1010U; CHECK(pump() == 0 && count_address(0U) == 1U && (frames_hi[count - 1U] & 0xFFFFUL) == 42U); /* После паузы повторяет актуальное изменение и отдельно ожидающий heartbeat. */
prepare(5U, 0U); Maska[0][0] = 1U; CAN_telemetry_service(); ECanaRegs.CANTA.all = 0U; CpuTimer1.InterruptCount = 100U; CAN_telemetry_service(); /* Потерянный timeout IRQ страхуется реальным временем Timer1 через 100 мс. */
CHECK(can_tx_failed && ECanaRegs.CANTRR.all == 1U && CanTimeOutErrorTR == 1); /* Программная отмена не ждёт аппаратного CANAA в блокирующем цикле. */
CAN_telemetry_service(); CHECK(CanTimeOutErrorTR == 1); /* Длительная аппаратная отмена не увеличивает счётчик ошибки на каждой итерации main. */
prepare(5U, 0U); Maska[0][0] = 1U; CHECK(pump() == 0); CpuTimer1.InterruptCount = 20U; modbus[0] = 2; CAN_telemetry_service(); ECanaRegs.CANTA.all = 0U; ECanaRegs.CANTRS.all = 0U; ECanaRegs.CANAA.all = 1U; CAN_telemetry_service(); /* Изменение, отменённое после старого успешного снимка, не должно затереть подтверждённый кэш. */
CHECK(can_last_hi[0] == 0xE0000000UL && can_backoff); /* Только реально доставленный прежний пакет остаётся подтверждённым. */
prepare(5U, 0U); CHECK(pump() == 0 && count == 0U); CpuTimer1.InterruptCount = 3500U; CHECK(pump() == 0 && count == 1U && address_at(0U) == 0x100U && (frames_hi[0] & 0xFFFFU) == 3U); /* Пустые маски не зависают; после задержки main отправляется последний heartbeat без пачки пропущенных секунд. */
CHECK(pump() == 0 && count == 1U); /* Повторная итерация main не дублирует уже поставленный heartbeat. */
prepare(5U, 0U); Maska[0][0] = 1U; Cancount[0] = 0; Cancount[1] = 0; CHECK(pump() == 0 && count == 0U); CpuTimer1.InterruptCount = 1000U; CHECK(pump() == 0 && count == 1U && address_at(0U) == 0x100U); /* Отключённые потоки не отключают независимый heartbeat узла. */
prepare(5U, 0U); Maska[0][7] = 0x8000U; modbus[127] = 0x55AA; CHECK(pump() == 0 && count == 1U && frames_hi[0] == 0xE07F55AAUL && frames_lo[0] == 0U); /* Конец карты R127 дополняется нулями без чтения data[128] и data[129]. */
prepare(5U, 0U); for (index = 0U; index < 8U; ++index) Maska[0][index] = 0xFFFFU; CHECK(pump() == 0 && count == 43U); /* Плотная маска сохраняет прежние 43 трёхсловных пакета вместо лишних 128 перекрывающихся кадров. */
for (index = 0U; index < count; ++index) CHECK(address_at(index) == index * 3U); /* Первый полный снимок покрывает ровно R0, R3, ... R126 в установленном формате. */
CpuTimer1.InterruptCount = 1300U; CHECK(pump() == 0 && count_address(0U) == 2U && count_address(126U) == 2U && count == 87U); /* Готовая очередь выдаёт всю карту за период, а не один кадр маски каждые 1,3 с. */
prepare(5U, 0U); CpuTimer1.InterruptCount = 0xFFFFFF00UL; started = CpuTimer1.InterruptCount; Maska[0][0] = 1U; CHECK(pump() == 0); CpuTimer1.InterruptCount = started + 1000U; CHECK(pump() == 0 && count_address(0x100U) == 1U); /* Секундный heartbeat переживает переполнение Timer1. */
CpuTimer1.InterruptCount = started + 1300U; CHECK(pump() == 0 && count_address(0U) == 2U); /* Период повторения данных тоже корректен через 32-битное переполнение. */
prepare(5U, 0U); Maska[0][0] = 1U; CHECK(pump() == 0); before = count; can_valid[7] = 0xFFFFU; can_backoff = can_heartbeat_due = 1U; wait = 1; CAN_telemetry_init(); CHECK(pump() == 0 && count == before + 1U); /* Повторная инициализация отбрасывает грязное состояние и заново отправляет первый снимок. */
prepare(5U, 0U); Maska[0][1] = 1U; Maska[1][0] = 1U; for (index = 0U; index <= 250U; ++index) { CpuTimer1.InterruptCount = index * 20U; modbus[16] = index; CHECK(pump() == 0); } /* Непрерывное изменение быстрого потока не должно вытеснить heartbeat и неизменный пакет другого потока. */
CHECK(count_address(16U) == 251U && count_address(0U) == 4U && count_address(0x100U) == 5U); /* За пять секунд доставляются все изменения, четыре состояния R0 и пять секундных heartbeat. */
prepare(5U, 0U); Maska[0][0] = 1U; CAN_telemetry_service(); CpuTimer1.InterruptCount = 50U; hardware_ack(); CAN_telemetry_service(); /* Подтверждает первый пакет через 50 мс после запуска, а не в нулевой момент передачи. */
CpuTimer1.InterruptCount = 1349U; CHECK(CAN_send(0, modbus, 0) == 2); CpuTimer1.InterruptCount = 1350U; CHECK(CAN_send(0, modbus, 0) == 1); /* Индивидуальные 1300 мс отсчитываются от ACK, поэтому ранний повтор через 1300 мс от запуска запрещён. */
prepare(5U, 0U); ECanaRegs.CANTOC.all = 1U; ECanaRegs.CANTRS.all = 1U; CANa_reset_err(); /* Вызывает фантомный ISR без аппаратного CANTOS0 во время исправной передачи. */
CHECK(!can_tx_failed && !CanTimeOutErrorTR && ECanaRegs.CANTRR.all == 0U && ECanaRegs.CANTOC.all == 1U && PieCtrlRegs.PIEIER9.all == 0x0030U); /* Не отменяет исправный кадр и восстанавливает прежние разрешения обоих CAN IRQ. */
prepare(5U, 0U); Maska[0][0] = 1U; ECanaRegs.CANTRR.all = 1U; ECanaMboxes.MBOX0.MDH.all = 0x11223344UL; CAN_telemetry_service(); CHECK(ECanaMboxes.MBOX0.MDH.all == 0x11223344UL && !wait); /* Даже без CANTRS незавершённая аппаратная отмена запрещает перезапись mailbox. */
prepare(5U, 0U); irq_mask = 1U; CHECK(CAN_send(0, modbus, 0) == 1 && irq_mask == 1U); /* Передатчик сохраняет уже запрещённые IRQ вызывающего кода вместо безусловного разрешения. */
puts("PASS: heartbeat 1 Hz; changed data; per-packet refresh 1300 ms; all modes; masks; ACK, timeout and backoff; wraparound; dirty RAM; bounds"); /* Подтверждает проверку реального production-кода без утверждения о запуске на физической плате. */
return 0; /* Все независимые поведенческие проверки завершились успешно. */
} /* Модель не требует подключения CAN-адаптера и не отправляет команды реальному оборудованию. */

View File

@@ -1,22 +0,0 @@
from pathlib import Path # Находим production-исходники относительно установленного теста либо явно выбранного проверяемого снимка.
import os # Подключаем runtime GCC только дочернему процессу теста.
import re # Извлекаем настоящий ISR с сохранением вложенных условий и без отдельной копии алгоритма ошибки.
import subprocess # Компилируем и выполняем поведенческую модель передатчика.
import sys # Разрешаем проверить изолированный снимок коммита вместо текущего рабочего дерева.
import tempfile # Создаём исполняемый файл проверки только во временном каталоге теста.
test = Path(__file__).resolve().parent # Производственная установка хранит этот файл в tests/can_telemetry.
root = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else test.parents[1] # При обычном запуске проверяет текущий проект прошивки.
source = (root / 'Source/Internal/ecan.c').read_text(encoding='utf-8') # Изменённый production-файл и русские комментарии обязаны быть корректным UTF-8.
clean = re.sub(r'/\*.*?\*/|//[^\n]*', '', source, flags=re.S) # Скобки комментариев не влияют на выделение ISR.
match = re.search(r'interrupt\s+void\s+CANa_reset_err\(void\)\s*\{', clean) # Отличаем определение обработчика от прототипа и регистрации IRQ.
assert match # Отсутствующий обработчик не считается успешной проверкой передачи.
end, depth = match.end(), 1 # Начинаем поиск после внешней открывающей скобки функции.
while depth: depth += (clean[end] == '{') - (clean[end] == '}'); end += 1 # Сохраняем все вложенные аппаратные проверки оригинала.
model = (test / 'model.c').read_text(encoding='utf-8').replace('/* TIMEOUT_ISR */', clean[match.start():end]) # Сама реализация телеметрии подключается напрямую через production-header.
compiler = os.environ.get('CC', 'C:/msys64/mingw64/bin/gcc.exe') # Поддерживаем установленный GCC и явный выбор другого совместимого компилятора.
environment = dict(os.environ); environment['PATH'] = str(Path(compiler).parent) + os.pathsep + environment.get('PATH', '') # Не меняем системный PATH пользователя или настройки CCS.
with tempfile.TemporaryDirectory(prefix='can-telemetry-166-') as directory: # Временные файлы не попадают в источник прошивки или Git-коммит.
cfile, exe = Path(directory) / 'test.c', Path(directory) / 'test.exe' # Отделяем исходник модели и её исполняемый результат.
cfile.write_text(model, encoding='utf-8') # Тестовые комментарии сохраняются в той же кодировке UTF-8.
subprocess.run([compiler, '-std=c99', '-O3', '-Wall', '-Wextra', '-Werror', '-I' + str(root / 'Source/Internal/Include'), str(cfile), '-o', str(exe)], env=environment, check=True) # Все предупреждения нового алгоритма считаются ошибками до создания коммита.
subprocess.run([str(exe)], env=environment, check=True, timeout=10) # Зависание очереди или нарушенный тайминг завершают проверку ошибкой.

View File

@@ -0,0 +1,58 @@
#include <stdint.h> /* Задаёт размеры 16- и 32-битных типов C28x в хостовой модели. */
#include <stdio.h> /* Печатает конкретное нарушенное условие и итог сценариев. */
#include <string.h> /* Обнуляет модель между независимыми сценариями. */
typedef uint16_t Uint16; /* Воспроизводит тип масок PIE, используемый обработчиком TI. */
typedef uint32_t Uint32; /* Воспроизводит упаковку двух технологических слов в MDH/MDL. */
#define interrupt /* Убирает только аппаратный атрибут функции при компиляции на ПК. */
#define EALLOW ((void)0) /* Модель не воспроизводит механизм защиты регистров ядра. */
#define EDIS ((void)0) /* Защита EALLOW проверяется сборкой настоящего модуля под TI. */
#define EINT ((void)0) /* Входы ISR вызывает сценарий, не поток операционной системы. */
#define DINT ((void)0) /* Отключение прерываний не требует имитации планировщика на ПК. */
#define M_INT9 0x0100U /* Маска CPU INT9 соответствует девятой группе PIE. */
#define MINT9 0x0100U /* Для проверки жизненного цикла сохраняет разрешение группы CAN. */
#define MG96 0x00DFU /* Исключает повторную вложенную обработку INTx6 той же группы. */
struct REG { uint32_t all; }; /* Представляет 32-битные регистры eCAN без зависимости от размеров long на хосте. */
struct ECAN_REGS { struct REG CANTA, CANAA, CANTRR, CANTRS, CANTOC, CANTOS; uint32_t CANTSC; }; /* Даёт рабочим функциям реальные имена используемых регистров. */
static volatile struct ECAN_REGS ECanaRegs; /* Позволяет наблюдать все записи production-функций при оптимизации O3. */
struct ECAN_MBOXES { struct { struct REG MDH, MDL; } MBOX0; }; /* Моделирует только штатный передатчик, который проверяет этот тест. */
static volatile struct ECAN_MBOXES ECanaMboxes; /* Содержимое mailbox остаётся доступным для проверок неизменности занятого кадра. */
static struct { struct { Uint16 all; } PIEIER9; union { unsigned all; struct { unsigned reserved:8, ACK9:1; } bit; } PIEACK; } PieCtrlRegs; /* Связывает запись всей маски PIEACK с битом подтверждения девятой группы в позиции 8. */
static Uint16 IER; /* Представляет маску разрешённых прерываний ядра. */
static int wait, CanTimeOutErrorTR; /* Повторяет состояние штатного передатчика и счётчик обнаруженных тайм-аутов. */
static void led1_toggle(void) {} /* Светодиод передачи не влияет на завершение аппаратной транзакции. */
/* PRODUCTION */
#define CHECK(condition) do { if (!(condition)) { printf("FAIL line %d: %s\n", __LINE__, #condition); return 1; } } while (0) /* Не отключает проверку условий при оптимизации или NDEBUG. */
static void reset_model(void) { /* Начинает сценарий без остаточных запросов и подтверждений предыдущего теста. */
memset((void *)&ECanaRegs, 0, sizeof(ECanaRegs)); /* Сбрасывает моделируемые аппаратные регистры CAN. */
memset((void *)&ECanaMboxes, 0, sizeof(ECanaMboxes)); /* Убирает ранее переданное содержимое mailbox. */
memset(&PieCtrlRegs, 0, sizeof(PieCtrlRegs)); /* Исключает ложное подтверждение PIE из предыдущего сценария. */
PieCtrlRegs.PIEIER9.all = 0x0030U; IER = M_INT9; /* Разрешает оба CAN-прерывания для проверки восстановления маски ISR. */
wait = 0; CanTimeOutErrorTR = 0; /* Повторяет начальные значения production-переменных. */
} /* Завершает начальную установку независимого сценария. */
int main(void) { /* Проверяет потерю ACK, фантомное прерывание, занятый mailbox и восстановление отправки. */
int words[128] = {0}; unsigned calls; /* Хранит данные кнопок и ограничивает имитацию повторных входов ISR. */
reset_model(); ECanaRegs.CANTOC.all = 1; ECanaRegs.CANTOS.all = 1; ECanaRegs.CANTRS.all = 5; wait = 1; /* Тайм-аут относится к mailbox 0, пока служебный mailbox 2 тоже занят. */
for (calls = 0; calls < 4 && ECanaRegs.CANTOS.all; calls++) { /* Повторяет IRQ, пока источник не снят, с явной защитой от зависания старого кода. */
CANa_reset_err(); /* Выполняет настоящий обработчик проекта, включая отмену TX и подтверждение PIE. */
ECanaRegs.CANTOS.all &= ECanaRegs.CANTOC.all; /* По SPRUI07 13.8.18.3 сброс TOC аппаратно снимает TOS и MTOF; одна запись CANTRR этого не гарантирует. */
} /* Проверяет, что обработчик освобождает процессор после первого тайм-аута. */
CHECK(calls == 1 && ECanaRegs.CANTOS.all == 0 && CanTimeOutErrorTR == 1); /* Повторные входы без снятия источника означают блокирование основного цикла. */
CHECK(ECanaRegs.CANTRR.all == 1 && ECanaRegs.CANTRS.all == 5); /* Отмена запрошена только для штатного TX; модель намеренно ещё не завершила аппаратную отмену. */
CHECK(PieCtrlRegs.PIEIER9.all == 0x0030U && PieCtrlRegs.PIEACK.bit.ACK9 == 1); /* ISR сохраняет прежние разрешения обоих каналов CAN и подтверждает свою группу. */
words[16] = 8; ECanaMboxes.MBOX0.MDH.all = 0x12345678UL; /* Подготавливает новое состояние кнопки при ещё занятом предыдущем кадре. */
CAN_send(0, words, 16); CHECK(ECanaMboxes.MBOX0.MDH.all == 0x12345678UL); /* Не допускает перезаписи данных до завершения отмены. */
ECanaRegs.CANTRS.all = 0; ECanaRegs.CANTRR.all = 0; ECanaRegs.CANAA.all = 1; ECanaRegs.CANTSC = 999; /* Имитирует аппаратное завершение отмены и подтверждение CANAA0. */
CAN_send(0, words, 16); /* Проверяет повторный запуск тем же реальным передатчиком после исчезновения тайм-аута. */
CHECK(ECanaRegs.CANTOC.all == 1 && ECanaRegs.CANTSC == 0 && ECanaRegs.CANTRS.all == 1); /* Новая передача вновь получает собственный отсчёт тайм-аута. */
CHECK(ECanaMboxes.MBOX0.MDH.all == 0xE0100008UL && ECanaMboxes.MBOX0.MDL.all == 0); /* После восстановления передаются свежие данные R16, а не старый нулевой кадр. */
reset_model(); ECanaRegs.CANTOC.all = 1; ECanaRegs.CANTRS.all = 1; /* Фантомное PIE-прерывание может прийти, когда начат следующий кадр без тайм-аута. */
CANa_reset_err(); CHECK(ECanaRegs.CANTRR.all == 0 && CanTimeOutErrorTR == 0 && ECanaRegs.CANTOC.all == 1); /* Не отменяет исправную передачу при уже снятом TOS0. */
reset_model(); ECanaRegs.CANTOC.all = 1; ECanaRegs.CANTOS.all = 1; /* Передача успела завершиться, но сценарий сохраняет зафиксированный флаг тайм-аута. */
CANa_reset_err(); CHECK(ECanaRegs.CANTRR.all == 0 && ECanaRegs.CANTOC.all == 0); /* Снимает контроль, не отменяя уже свободный mailbox. */
reset_model(); ECanaRegs.CANTRR.all = 1; ECanaMboxes.MBOX0.MDH.all = 0x11223344UL; /* Аппаратная отмена ещё активна, даже если CANTRS уже снят. */
CAN_send(0, words, 16); CHECK(ECanaMboxes.MBOX0.MDH.all == 0x11223344UL && ECanaRegs.CANTRS.all == 0); /* Не запускает повторный кадр до снятия CANTRR0. */
reset_model(); wait = 1; ECanaRegs.CANTA.all = 1; /* Проверяет обычную передачу после успешного подтверждения предыдущего кадра. */
CAN_send(0, words, 16); CHECK(ECanaRegs.CANTRS.all == 1 && ECanaRegs.CANTOC.all == 1); /* Исправление не блокирует штатный обмен при наличии CAN ACK. */
puts("PASS CAN timeout: IRQ released, phantom ignored, busy mailbox preserved, TX recovered"); /* Сообщает только о проверенных сценариях программной модели. */
return 0; /* Подтверждает прохождение всех проверок оптимизированного рабочего кода. */
} /* Завершает тест без доступа к реальному USB-CAN или питанию платы. */

View File

@@ -0,0 +1,31 @@
"""Проверяет тайм-аут штатного CAN на настоящих функциях проекта 166.""" # Модель не заменяет испытание аппаратного eCAN.
import os # Передаёт пути библиотек GCC только дочерним процессам.
from pathlib import Path # Находит исходники независимо от рабочего каталога запуска.
import re # Удаляет комментарии и выделяет тела проверяемых функций.
import subprocess # Запускает компилятор и проверяет поведенческий результат модели.
import tempfile # Изолирует сгенерированные файлы от образов прошивки пользователя.
import sys # Позволяет проверить отдельно сохранённую старую версию ecan.c.
root = Path(__file__).resolve().parents[2] # Корень проекта содержит рабочую реализацию eCAN.
source_path = Path(sys.argv[1]) if len(sys.argv) > 1 else root / "Source/Internal/ecan.c" # Дополнительный путь нужен для проверки чувствительности к старой ошибке.
source = re.sub(r"/\*.*?\*/|//[^\n]*", "", source_path.read_text(encoding="utf-8"), flags=re.S) # Комментарии со скобками не участвуют в выделении функций.
def function(name): # Извлекает текущий алгоритм вместо поддержания отдельной копии в тесте.
match = re.search(r"(?:interrupt\s+)?void\s+" + name + r"\([^;{}]*\)\s*\{", source) # Отличает определение от вызова и объявления.
assert match is not None, name # Исчезновение функции должно останавливать проверку.
end, depth = match.end(), 1 # Начинает поиск после открывающей скобки тела.
while depth: # Учитывает вложенные условия до закрытия внешнего блока.
depth += (source[end] == "{") - (source[end] == "}") # Считает только скобки кода без комментариев.
end += 1 # Включает найденную закрывающую скобку в результат.
return source[match.start():end] # Сохраняет все реальные обращения к регистрам и условия.
model = Path(__file__).with_name("model.inc").read_text(encoding="utf-8") # Загружает минимальное аппаратное окружение и сценарии потери ACK.
model = model.replace("/* PRODUCTION */", function("CAN_send") + "\n" + function("CANa_reset_err")) # Проверяет передачу и прерывание в одном жизненном цикле.
compiler = os.environ.get("CC", "C:/msys64/mingw64/bin/gcc.exe") # Использует установленный GCC, сохраняя возможность явно выбрать другой.
environment = dict(os.environ) # Не меняет окружение приложения и проекта CCS.
environment["PATH"] = str(Path(compiler).parent) + os.pathsep + environment.get("PATH", "") # Делает библиотеки MinGW доступными тестовой программе.
with tempfile.TemporaryDirectory(prefix="can-timeout-") as directory: # Удаляет только собственные временные файлы после завершения проверки.
c_path, exe_path = Path(directory) / "test.c", Path(directory) / "test.exe" # Разделяет исходник модели и исполняемый результат.
c_path.write_text(model, encoding="utf-8") # Сохраняет русские пояснения модели в требуемой кодировке.
subprocess.run([compiler, "-std=c99", "-O3", "-Wall", "-Wextra", "-Wno-unused-parameter", "-Werror", str(c_path), "-o", str(exe_path)], env=environment, check=True) # Проверяет реальные функции под оптимизацией и без скрытых предупреждений.
subprocess.run([str(exe_path)], env=environment, check=True, timeout=10) # Зависание и нарушенное условие делают сценарий неуспешным.

View File

@@ -0,0 +1,83 @@
"""Проверяет частоту подтверждения R3 на настоящих масках и планировщике 166.""" # Проверка выполняется на ПК без отправки команд плате.
from pathlib import Path # Находит исходники проекта и отдельно подготовленный вариант маски.
import os # Передаёт библиотеки GCC только дочерним процессам проверки.
import re # Извлекает определения и блоки C без комментариев.
import subprocess # Компилирует и исполняет реальные алгоритмы прошивки в модели времени.
import sys # Позволяет проверить старый и исправленный файлы одинаковым сценарием.
import tempfile # Ограничивает результаты компиляции временным каталогом.
root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).resolve().parents[2] # Установленная проверка по умолчанию использует свой проект прошивки.
measure = Path(sys.argv[2]) if len(sys.argv) > 2 else root / 'Source/Internal/measure.c' # Подмена только этого файла позволяет воспроизвести ошибку до применения исправления.
def clean(path): # Удаляет комментарии перед поиском парных скобок C.
raw = path.read_bytes() # Сохраняет возможность чтения старых исходников Windows-1251.
source = raw.decode('utf-8') if _utf8(raw) else raw.decode('cp1251') # Не теряет русские комментарии исходника при выборе кодировки.
return re.sub(r'/\*.*?\*/|//[^\n]*', '', source, flags=re.S) # Скобки в пояснениях не меняют границы блока алгоритма.
def _utf8(raw): # Отличает старую Windows-кодировку от требуемого UTF-8 без замены символов.
try: # Корректный UTF-8 имеет приоритет перед совместимой однобайтовой кодировкой.
raw.decode('utf-8'); return True # Успешное декодирование разрешает чтение современного исходника.
except UnicodeDecodeError: # Старый файл будет прочитан в его исходной кодировке.
return False # Не выдаёт повреждённые комментарии за исходный текст.
def block(source, start): # Выделяет настоящий блок с вложенными условиями.
cursor, depth = source.index('{', start) + 1, 1 # Начинает после открывающей скобки внешнего блока.
while depth: # Ищет соответствующую закрывающую скобку.
depth += (source[cursor] == '{') - (source[cursor] == '}') # Учитывает только скобки очищенного C-кода.
cursor += 1 # Включает завершающую скобку в извлечённый текст.
return source[start:cursor] # Проверка не поддерживает отдельную копию алгоритма прошивки.
package = clean(root / 'Source/Internal/Include/package.h') # Значения режимов и индексов потоков берутся из проекта 166.
names = ('dsk_COMM', 'dsk_BKSD', 'dsk_BKST', 'dsk_EPLT', 'dsk_SHKF', 'dsk_LOAD', 'm_FAST', 'm_SLOW') # Модели нужны только константы, используемые масками.
defines = '\n'.join(re.search(r'^#define\s+' + name + r'\s+\d+', package, re.M).group() for name in names) # Не дублирует номера плат в тестовой карте.
measure_source = clean(measure) # Выбирает проверяемую версию Init_packMask.
mask_function = block(measure_source, measure_source.index('void Init_packMask(')) # Состав быстрой и медленной рассылки полностью берётся из прошивки.
main = clean(root / 'Source/Internal/main.c') # Последовательность передачи берётся из текущего рабочего main.c.
scheduler = block(main, main.index('if(CanGO)')) # Проверяет настоящий учёт занятого mailbox и продвижение адресов.
base_tick = int(re.search(r'^#define\s+CANPOWSE\s+(\d+)', clean(root / 'Source/Internal/Include/measure.h'), re.M).group(1)) # При READY_FREQ=1000 Гц делитель CANPOWSE равен числу миллисекунд базового шага.
model = r'''
#include <stdio.h> /* Выводит измеренные моделью интервалы подтверждения. */
#include <string.h> /* Сбрасывает состояние перед каждым сочетанием периодов EEPROM. */
/* DEFINES */
static int Desk, Kurrent, tpl_cans, CanGO, modbus[131], busy; /* Запас слов повторяет трёхсловное чтение у границы R127. */
static unsigned int Maska[2][8], cancount[2], cancell[2], now, received_at, max_gap, received_count; /* Каждое состояние соответствует маскам, очередям и статистике RX. */
#define Cancount (modbus+0x60) /* R96/R97 задают периоды fast/slow в базовых интервалах CANPOWSE. */
#define CHECK(condition) do { if (!(condition)) { printf("FAIL line %d: %s\n", __LINE__, #condition); return 1; } } while (0) /* Ошибка остаётся видимой и при включённой оптимизации. */
static int CAN_send(int port, int data[], int address) { /* Моделирует только принятие кадра свободным mailbox, не аппаратный ACK. */
(void)port; /* Номер порта не влияет на порядок технологических кадров. */
if(busy) return 0; /* Второй поток в том же тике должен повторить попытку позднее. */
busy=1; /* Один передатчик одновременно принимает только один кадр. */
if(address<=3 && address+2>=3) { /* R3 передаётся и отдельным кадром, и внутри трёхсловного фрагмента. */
if(data[3] != (int)now) return 0; /* Проверяет передачу свежего слова, изменённого перед текущим шагом. */
if(now-received_at>max_gap) max_gap=now-received_at; /* Наблюдает худшее ожидание следующего реального кадра с R3. */
received_at=now; received_count++; /* Фиксирует только принятый передатчиком регистр. */
} /* Завершает наблюдение очередного фрагмента. */
return 1; /* Успешная постановка позволяет настоящему main продвинуть адрес. */
} /* Модель намеренно не заменяет проверку физического CAN на плате. */
/* MASK */
static void step(void) { int i, mask; /* Локальные переменные повторяют контекст фонового цикла main.c. */
/* SCHEDULER */
} /* Выполняет один базовый интервал настоящего планировщика. */
int main(void) { /* Покрывает штатные периоды 166 и варианты из EEPROM, включая отключённый медленный поток. */
const unsigned periods[][2]={{2,101},{10,101},{12,50},{2,0}}; /* Периоды заданы в тиках CANPOWSE: 2 — штатный быстрый делитель VPU-166, 101 — медленный. */
unsigned scenario, tick; /* Сценарии независимы, каждый охватывает сто секунд модельного времени. */
Desk=dsk_EPLT; Init_packMask(); /* Строит реальную карту задатчика выбранной версии прошивки. */
CHECK((Maska[m_FAST][0]&15)==15 && !(Maska[m_SLOW][0]&15)); /* R0–R3 должны подтверждаться быстрым потоком независимо от периода параметров. */
CHECK(Maska[m_FAST][1]==1 && Maska[m_SLOW][6]==0x1FF && Maska[m_SLOW][7]==0xE000); /* Кнопки R16 и медленные параметры R96–R104/R125–R127 сохраняют состав. */
for(scenario=0;scenario<sizeof(periods)/sizeof(periods[0]);scenario++) { /* Проверяет передачу R3 при разных сохранённых настройках платы. */
memset(cancount,0,sizeof(cancount)); memset(cancell,0,sizeof(cancell)); /* Начинает каждый сценарий с первого адреса и нулевого срока. */
received_at=max_gap=received_count=now=0; /* Старые измерения не сокращают наблюдаемую задержку нового сценария. */
Cancount[0]=(int)periods[scenario][0]; Cancount[1]=(int)periods[scenario][1]; /* Использует заданные периоды без сброса EEPROM к defaults. */
for(tick=1;tick<=5000;tick++) { /* Пять тысяч шагов по 20 мс включают много полных обходов медленной очереди. */
now=tick; modbus[3]=(int)tick; busy=0; CanGO=1; step(); /* Меняет R3 перед каждым шагом и завершает предыдущую аппаратную передачу. */
} /* В статистику входят начальное ожидание и все последующие интервалы. */
CHECK(received_count>0 && max_gap<=3*(periods[scenario][0]+1)); /* Три быстрых кадра R0–R2/R3–R5/R16–R18 ограничивают ожидание независимо от slow. */
printf("PASS fast=%u slow=%u: R3 max=%u ms, updates=%u\n",periods[scenario][0],periods[scenario][1],max_gap*BASE_TICK,received_count); /* Переводит тики в миллисекунды базового таймера платы. */
} /* Все сочетания периодов обязаны давать частое подтверждение R3. */
return 0; /* Успех означает прохождение модели, а не испытание физического устройства. */
} /* Завершает регрессию задержки обратной связи VPU. */
''' # Вставки ниже исполняют текущий код прошивки, а не его переписанную модель.
model = model.replace('/* DEFINES */', defines + f'\n#define BASE_TICK {base_tick} /* Длительность базового шага в миллисекундах при таймере 1000 Гц. */').replace('/* MASK */', mask_function).replace('/* SCHEDULER */', scheduler) # Связывает реальные маски и планировщик с моделью одного занятого передатчика.
compiler = Path(os.environ.get('CC', 'C:/msys64/mingw64/bin/gcc.exe')) # Использует установленный GCC без загрузки инструментов.
environment = dict(os.environ, PATH=str(compiler.parent) + os.pathsep + os.environ.get('PATH', '')) # Делает библиотеки компилятора доступными только дочерним программам.
with tempfile.TemporaryDirectory(prefix='vpu-r3-rx-') as directory: # Удаляет только собственные временные результаты после проверки.
source, executable = Path(directory) / 'test.c', Path(directory) / 'test.exe' # Разделяет C-код проверки и исполняемую программу.
source.write_text(model, encoding='utf-8') # Все создаваемые исходники и русские комментарии сохраняются в UTF-8.
subprocess.run([str(compiler), '-std=c99', '-O3', '-Wall', '-Wextra', '-Werror', str(source), '-o', str(executable)], env=environment, check=True) # Требует чистой компиляции настоящих алгоритмов при оптимизации.
subprocess.run([str(executable)], env=environment, check=True, timeout=10) # Зависание либо нарушение ограничения задержки проваливает регрессию.

View File

@@ -1,83 +0,0 @@
#include <stdint.h> /* Задаёт независимые от host ABI размеры слов паспорта. */
#include <stdio.h> /* Сообщает точную строку нарушенной регрессии. */
#include <string.h> /* Сбрасывает только модель RS-запроса между сценариями. */
#include "crc16.h" /* Использует настоящую CRC прошивки вместо имитации проверки ответа. */
#include "firmware_info.h" /* Подключает действительный адаптер границ 0x1200..0x120B. */
#include "../src/firmware_info_static.c" /* Проверяет тот же общий упаковщик, который компилируется в message.c. */
#define CHECK(value) do { if (!(value)) { printf("FAIL line %d: %s\n", __LINE__, #value); return 1; } } while (0) /* Сохраняет проверки при оптимизации O3. */
#define CMD_MODBUS_3 3U /* Код чтения holding-регистров остаётся прежним. */
#define CMD_MODBUS_6 6U /* Код одиночной записи используется для проверки защиты паспорта. */
#define CNTRL_ADDR 1U /* Тестирует конкретный корректный адрес узла без изменения производственной адресации. */
#define ANSWER_LEN 128 /* Повторяет размер прежней технологической карты. */
#define true 1 /* Повторяет 16-битное представление флага готовности ответа C28x. */
#define LOBYTE(value) ((value) & 255U) /* Сохраняет порядок младшего октета CRC в RS-ответе. */
#define HIBYTE(value) (((value) >> 8) & 255U) /* Сохраняет старший октет контрольной суммы Modbus. */
typedef struct { unsigned int RS_Header[400], buffer[400], flag_TIMEOUT_to_Send; } RS_DATA; /* Даёт настоящим обработчикам только используемые поля UART-окружения. */
static unsigned int sent, irq_mask, irq_calls, reads, writes, command, command_events; /* Наблюдает длину кадра, защиту IRQ и возможное повреждение старого backend. */
static int modbus[ANSWER_LEN + 1]; /* Проверяет чтение технологических регистров прежним маршрутом. */
static struct { unsigned int all; } Modbus[ANSWER_LEN + 1]; /* Поддерживает прежний интерфейс записи в таблицу параметров. */
static unsigned int __disable_interrupts(void) { unsigned int previous = irq_mask; irq_mask = 1U; ++irq_calls; return previous; } /* Выявляет лишнюю блокировку IRQ при чтении неизменяемого паспорта. */
static void __restore_interrupts(unsigned int previous) { irq_mask = previous; } /* Проверяет сохранение исходной маски для снимков логгера. */
static void RS_Send(RS_DATA *rs, unsigned int *data, unsigned long length) { (void)rs; (void)data; sent = (unsigned int)length; } /* Перехватывает готовый кадр без реального UART. */
unsigned int ump_log_read(unsigned int address) { ++reads; return address == 0x1000U ? 0x554DU : address; } /* Делает обращения к старому логгеру видимыми и узнаваемыми. */
int ump_log_write(unsigned int address, unsigned int value) { (void)address; (void)value; ++writes; return 1; } /* Любая ошибочная запись паспорта в backend станет видна тесту. */
static void UMP_commands_write(unsigned int value) { command = value; } /* Сохраняет наблюдение штатной команды регистра 127. */
static void ump_log_rs(void) { ++command_events; } /* Проверяет сохранение прежней регистрации команды RS. */
#include "ump_can_protocol.h" /* Тестирует реальный адаптер CAN 166, сохранивший двенадцатисловный логгер v4. */
#include "include/balsam167_eeprom_block.h" /* Использует общий с 167 контракт FC03: окно 0x8000..0xBFFF, до 120 слов AT25256B за запрос. */
static unsigned int spi_reads, spi_address, spi_bytes, spi_blocked; /* Наблюдает число SPI-транзакций, словный адрес, длину в октетах и маску IRQ во время чтения. */
static void Seeprom_read(unsigned int address, unsigned int *data, unsigned int length) /* Имитирует только SPI-периферию с независимым узнаваемым содержимым всех 32 КиБ. */
{ /* Производственный обработчик, упаковка и CRC остаются настоящими. */
unsigned int index; /* Перебирает аппаратные 16-битные слова запрошенного блока. */
++spi_reads; spi_address = address; spi_bytes = length; spi_blocked |= irq_mask; /* Выявляет лишние SPI-чтения и запрет Timer2 IRQ внутри нового маршрута. */
for (index = 0U; index < length / 2U; ++index) data[index] = (address + index) ^ 0xA55AU; /* Эталон зависит от полного адреса, поэтому обнаруживает повтор блока и ошибку смещения. */
} /* Модель не записывает EEPROM и не подменяет поведение UART. */
/* PRODUCTION_MODBUS */
static void request(RS_DATA *rs, unsigned int address, unsigned int count) /* Формирует уже проверенный транспортом заголовок для настоящих обработчиков. */
{ /* Здесь не подменяется алгоритм чтения или упаковки ответа. */
memset(rs, 0, sizeof(*rs)); sent = 0; /* Не переносит данные предыдущего ответа в новый запрос. */
rs->RS_Header[2] = address >> 8; rs->RS_Header[3] = address & 255U; /* Упаковывает исходный адрес старшим октетом вперёд. */
rs->RS_Header[4] = count >> 8; rs->RS_Header[5] = count & 255U; /* Упаковывает число слов либо значение одиночной записи. */
} /* Приём полного CRC-запроса остаётся вне этой локальной проверки обработчиков. */
int main(void) /* Проверяет перенос паспорта и сохранность старых границ протокола. */
{ /* Эталон задан независимо от макроса общего C-инициализатора. */
const unsigned int expected[12] = {1, 1, 0, 0, 2026, 0x0A10, 0x0834, 22, 0x3132, 0x3334, 0x6162, 0x6364}; /* Формат v1, версия 1.0.0, UTC 2026-10-16 08:52:22, Build ID 1234abcd. */
RS_DATA rs; unsigned int words[UMP_CAN_MAX_WORDS], error, i, before; /* Использует те же ёмкости ответа, что действующий служебный CAN. */
for (before = 0U; before < 16384U; before += 120U) { /* Полный дамп 32 КиБ должен занять 137 запросов вместо 16384 запросов PEEK. */
unsigned int count = 16384U - before < 120U ? 16384U - before : 120U; /* Последний блок содержит только оставшиеся 64 слова, не выходя за физическую границу. */
request(&rs, 0x8000U + before, count); ReceiveCommandModbus3(&rs); /* Вызывает настоящий новый маршрут FC03 по всему окну, включая последний блок. */
CHECK(sent == count * 2U + 5U && rs.buffer[1] == 3U && rs.buffer[2] == count * 2U); /* Ответ имеет точную длину и не содержит нулей после CRC. */
CHECK(spi_address == before && spi_bytes == count * 2U && !spi_blocked && !irq_calls); /* SPI получает правильные единицы адреса и длины при разрешённых прерываниях Timer2. */
CHECK(get_crc_16(0xFFFFU, rs.buffer, sent) == 0U); /* Проверяет настоящую CRC каждого полного кадра независимым проходом по всем октетам. */
for (i = 0U; i < count; ++i) CHECK(((rs.buffer[3U + i * 2U] << 8) | rs.buffer[4U + i * 2U]) == ((before + i) ^ 0xA55AU)); /* Сверяет каждое слово и порядок октетов после расширения UART-буфера на месте. */
} /* Все 16384 слова должны быть доступны без чтения соседней памяти. */
CHECK(spi_reads == 137U && reads == 0U && writes == 0U); /* Блочный маршрут не обращается к backend логгера и не выполняет записи. */
request(&rs, 0xBFFFU, 1U); ReceiveCommandModbus3(&rs); CHECK(sent == 7U && spi_address == 0x3FFFU && spi_bytes == 2U); /* Последнее слово EEPROM остаётся доступно отдельным запросом. */
before = spi_reads; /* Запоминает число разрешённых чтений до проверки всех ошибочных границ. */
request(&rs, 0xBFFFU, 2U); ReceiveCommandModbus3(&rs); CHECK(sent == 5U && rs.buffer[1] == 0x83U && rs.buffer[2] == 2U); /* Пересечение конца EEPROM возвращает исключение до обращения к SPI. */
request(&rs, 0x8000U, 0U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Нулевой блок не начинает зависающее аппаратное ожидание. */
request(&rs, 0x8000U, 121U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Максимум 120 слов соответствует контракту SETGUI и безопасной длине ответа 245 октетов. */
request(&rs, 0xC000U, 1U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Адрес сразу после окна не считается первым словом другой EEPROM. */
request(&rs, 0xFFFFU, 120U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Переполнение 16-битного адреса не обходит ограничение диапазона. */
request(&rs, 0x8000U, 0x8001U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Маркер M95M02 не принимается прошивкой с историческим 16-битным SPI-драйвером. */
request(&rs, 0x7FFFU, 1U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Адрес перед окном остаётся запрещённым в старой карте регистров. */
CHECK(spi_reads == before && get_crc_16(0xFFFFU, rs.buffer, sent) == 0U); /* Неверные запросы не читают SPI и получают корректное исключение с CRC. */
request(&rs, 0x1200U, 12U); ReceiveCommandModbus3(&rs); CHECK(sent == 29U && rs.buffer[1] == 3U && rs.buffer[2] == 24U); /* Полный паспорт отвечает стандартным FC03 без хвостовых нулей. */
for (i = 0; i < 12U; ++i) CHECK(((rs.buffer[3 + i * 2] << 8) | rs.buffer[4 + i * 2]) == expected[i]); /* Проверяет все поля, порядок символов и октетов независимо от производственной упаковки. */
CHECK(get_crc_16(0xFFFFU, rs.buffer, sent) == 0U && reads == 0U && irq_calls == 0U); /* Паспорт защищён настоящей CRC и не обращается к логгеру или маске IRQ. */
request(&rs, 0x120BU, 1U); ReceiveCommandModbus3(&rs); CHECK(sent == 7U && rs.buffer[3] == 0x63U && rs.buffer[4] == 0x64U); /* Последнее слово поддерживает частичное чтение в пределах контракта 167. */
request(&rs, 0x120BU, 2U); ReceiveCommandModbus3(&rs); CHECK(sent == 5U && rs.buffer[1] == 0x83U && rs.buffer[2] == 2U); /* Пересечение верхней границы не раскрывает соседнюю память. */
request(&rs, 0x11FFU, 2U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Запрос из несуществующего регистра перед паспортом не считается допустимым. */
request(&rs, 0x1200U, 0U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Пустое чтение не принимает произвольный контекст за паспорт. */
request(&rs, 0xFFFFU, 12U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Проверка диапазона выдерживает переполнение 16-битного адреса. */
request(&rs, 0x1200U, 99U); ReceiveCommandModbus6(&rs); CHECK(sent == 5U && rs.buffer[1] == 0x86U && rs.buffer[2] == 2U && writes == 0U); /* Запись паспорта явно отвергается, не попадая в сохранённые параметры или логгер. */
CHECK(ump_can_prepare(3U, 0x1200U, 12U, words, &error) == 12U && error == 0U); /* Служебный CAN читает тот же паспорт без переноса остального драйвера 167. */
for (i = 0; i < 12U; ++i) CHECK(words[i] == expected[i]); /* Исключает расхождение паспорта между RS и CAN. */
ump_can_prepare(3U, 0x120BU, 2U, words, &error); CHECK(error == 2U); ump_can_prepare(6U, 0x1200U, 0U, words, &error); CHECK(error == 2U && writes == 0U); /* CAN также запрещает выход за паспорт и запись в него. */
modbus[5] = 0x1234; request(&rs, 5U, 1U); ReceiveCommandModbus3(&rs); CHECK(sent == 10U && rs.buffer[3] == 0x12U && rs.buffer[4] == 0x34U); /* Исторический ответ технологического регистра и его хвост сохранены. */
request(&rs, 0x1000U, 16U); ReceiveCommandModbus3(&rs); CHECK(sent == 37U && rs.buffer[3] == 0x55U && rs.buffer[4] == 0x4DU && reads == 16U && irq_calls == 1U && irq_mask == 0U); /* Снимок статуса логгера сохраняет прежний маршрут и защиту IRQ. */
before = reads; CHECK(ump_can_prepare(3U, UMP_LOG_DATA, 12U, words, &error) == 12U && error == 0U && reads == before + 12U && UMP_LOG_WORDS == 12U); /* Текущая запись v4 остаётся двенадцатисловной и не заменяется реализацией 167. */
request(&rs, 127U, 0x55U); ReceiveCommandModbus6(&rs); CHECK(command == 0x55U && command_events == 1U && sent == 10U); /* Штатная команда управления проходит прежний обработчик без изменения её смысла. */
puts("PASS: firmware-info RS/CAN, all 12 words, CRC, bounds, read-only, legacy registers and logger v4"); /* Подтверждает проверенный перенос без заявления об испытании аппаратной платы. */
return 0; /* Завершает все поведенческие сценарии реального кода обработчиков. */
} /* Остальные аппаратные алгоритмы не участвуют в тесте и не изменяются доработкой. */

View File

@@ -1,57 +0,0 @@
"""Проверяет точечный перенос паспорта 167 в действующие обработчики 166.""" # Использует реальные функции, общий статический порт и независимый эталон регистра.
import importlib.util # Загружает проверку встроенного паспорта без запуска конверсии или сборки.
import json # Формирует независимый тестовый manifest.
import os # Подключает runtime установленного GCC только дочерним процессам.
from pathlib import Path # Находит production-исходники и общую библиотеку относительно теста.
import re # Извлекает функции Modbus без копирования их реализации в тест.
import struct # Создаёт минимальный SCI8 с известными загрузочными словами.
import subprocess # Компилирует настоящие обработчики под оптимизацией O3.
import sys # Подключает общий анализатор паспорта, который использует SetGUI.
import tempfile # Изолирует временные заголовки и исполняемые файлы от выпуска прошивки.
ROOT = Path(__file__).resolve().parents[2] # Корень 166 содержит проверяемый message.c.
TEMPLATES = ROOT.parent / "newProject/templates" # Использует ту же общую зависимость, что проект 167 и текущая сборка.
def load(name, path): # Импортирует проектный скрипт без его CLI и побочных эффектов postbuild.
spec = importlib.util.spec_from_file_location(name, path); module = importlib.util.module_from_spec(spec) # Создаёт независимый модуль с точным путём источника.
spec.loader.exec_module(module); return module # Возвращает реальные функции проверки версии и SCI8.
builder = load("balsam166_build", ROOT / "UKSSTMS320F28335/build_identity.py") # Проверяет именно выпускной валидатор проекта.
prepare = load("balsam166_prepare", ROOT / "UKSSTMS320F28335/prepare_identity.py") # Проверяет тот же парсер SemVer, что выполняет CCS prebuild.
source = re.sub(r"/\*.*?\*/|//[^\n]*", "", (ROOT / "Source/Internal/message.c").read_text(encoding="utf-8"), flags=re.S) # Исключает скобки из комментариев при выделении определений.
def function(name): # Извлекает рабочую функцию с произвольной вложенностью условий.
match = re.search(r"(?:static\s+)?void\s+" + name + r"\([^;{}]*\)\s*\{", source); assert match, name # Не принимает вызов или прототип за определение.
end, depth = match.end(), 1 # Начинает подсчёт после открывающей скобки функции.
while depth: depth += (source[end] == "{") - (source[end] == "}"); end += 1 # Находит внешнюю закрывающую скобку настоящего алгоритма.
return source[match.start():end] # Сохраняет все операторы производственного обработчика.
model = Path(__file__).with_name("port_test.c").read_text(encoding="utf-8") # Окружение содержит только UART/IRQ и наблюдаемый backend логгера.
model = model.replace("/* PRODUCTION_MODBUS */", "\n".join(function(name) for name in ("modbus_exception", "ReceiveCommandModbus3", "ReceiveCommandModbus6"))) # Подставляет реальный код, который попадёт в прошивку.
compiler = os.environ.get("CC", "C:/msys64/mingw64/bin/gcc.exe") # Сохраняет возможность запускать тест доступным C99-компилятором.
environment = dict(os.environ); environment["PATH"] = str(Path(compiler).parent) + os.pathsep + environment.get("PATH", "") # Не меняет окружение CCS или приложения.
with tempfile.TemporaryDirectory(prefix="firmware166-test-") as directory: # Удаляет только собственные временные данные теста.
temporary = Path(directory); cfile = temporary / "port.c"; exe = temporary / "port.exe" # Разделяет C-модель и результат исполнения.
values = dict(zip(("FIRMWARE_VERSION_MAJOR", "FIRMWARE_VERSION_MINOR", "FIRMWARE_VERSION_PATCH", "FI_YEAR", "FI_MONTH", "FI_DAY", "FI_HOUR", "FI_MINUTE", "FI_SECOND"), (1, 0, 0, 2026, 10, 16, 8, 52, 22))) # Детерминированный паспорт независим от времени запуска теста.
values.update({f"FI_ID_{i}": ord(char) for i, char in enumerate("1234abcd")}) # Даёт известную последовательность ASCII-слов для проверки порядка октетов.
(temporary / "firmware_identity.h").write_text("\n".join(f"#define {name} {value}U /* Проверочная константа паспорта v1. */" for name, value in values.items()), encoding="utf-8") # Сохраняет fixture заголовка в требуемой кодировке.
cfile.write_text(model, encoding="utf-8") # Сохраняет извлечённые русские исходники без изменения production-файла.
command = [compiler, "-std=c99", "-O3", "-Wall", "-Wextra", "-Werror", "-Wno-unused-function", "-I" + str(TEMPLATES / "c/set-protocol"), "-I" + str(temporary), "-I" + str(ROOT / "Source/Internal/Include"), "-I" + str(TEMPLATES / "c/firmware-info/include"), str(cfile), str(ROOT / "Source/Internal/crc16.c"), "-o", str(exe)] # Проверяет типы и реальные CRC вместе с обработчиками RS/CAN.
subprocess.run(command, env=environment, check=True); subprocess.run([str(exe)], env=environment, check=True, timeout=10) # Ошибка типов, поведения или зависание делают тест неуспешным.
metadata = {"schema": 1, "product": "Balsam-166-PM35", "target": "TMS320F28335", "version": [1, 0, 0], "build_id": "1234abcd", "built_at_utc": "2026-10-16T08:52:22+00:00", "identity_contract": 1, "identity_register_count": 12} # Фиксирует весь контракт manifest, который должен принять существующий GUI.
words = [1, 1, 0, 0, 2026, 0x0A10, 0x0834, 22, 0x3132, 0x3334, 0x6162, 0x6364] # Независимый эталон совпадает с показанным протоколом, а не берётся из C-инициализатора.
data = b"\xaa\x08" + bytes(20) + struct.pack("<HHH12HH", 12, 0, 0x8000, *words, 0) # Создаёт корректный SCI8 с одним 12-словным блоком и терминатором.
image = temporary / "firmware.bin"; image.write_bytes(data); builder.verify_embedded(image, metadata) # Выпуск должен принять образ с достоверно совпавшим паспортом.
for broken in (data[:-2], data + b"\0\0", data[:28] + bytes(24) + data[52:], bytes(32509)): # Проверяет усечённый файл, хвост, чужие слова паспорта и пересечение параметров EEPROM.
image.write_bytes(broken) # Подменяет только временный проверяемый образ.
try: builder.verify_embedded(image, metadata) # Настоящий postbuild-валидатор должен отклонить каждый дефект.
except (ValueError, struct.error): pass # Ожидаемый отказ подтверждает защиту выпуска.
else: raise AssertionError("Invalid or stale SCI8 was accepted") # Не допускает фиктивного manifest поверх старой прошивки.
image.write_bytes(data); settings = temporary / "metadata.json"; settings.write_text(json.dumps(metadata), encoding="utf-8") # Восстанавливает корректный временный выпуск для проверки GUI.
sys.path.insert(0, str(TEMPLATES / "c/firmware-info/tools")); from identity import seal # Использует общий формат манифеста, применяемый в 167.
seal(image, settings) # Подписывает точные байты тестового BIN общим алгоритмом SHA-256.
native = ROOT.parent / "newProject/SETGUI/src/gui_desktop/native/setprotocol.dll" # Существующий GUI содержит ту же native-библиотеку CRC, которую импортирует анализатор паспорта.
if native.is_file(): os.environ.setdefault("SETPROTOCOL_LIBRARY", str(native)) # Сохраняет явно заданный путь пользователя и подключает установленный GUI без изменения файлов зависимости.
gui_python = ROOT.parent / "newProject/SETGUI/third_party/templates/python" # Проверяет фактическую vendored-копию анализатора текущего SetGUI, которая может отличаться от основной ветки templates.
sys.path.insert(0, str(gui_python if (gui_python / "set_devices/firmware_identity.py").is_file() else TEMPLATES / "python")); from set_devices.firmware_identity import inspect_image, decode_identity # Без установленного GUI использует тот же общий контракт библиотеки.
identity = inspect_image(image, expected_product="Balsam-166-PM35"); assert identity == decode_identity(words) # Версия из BIN и версия из ответа платы должны сравниваться без адаптации GUI.
try: inspect_image(image, expected_product="Balsam-167-PM35") # Перенос проверки не должен разрешать выдавать прошивку 166 за 167.
except ValueError: pass # Существующая защита GUI отличает product двух плат.
else: raise AssertionError("166 firmware accepted as 167") # Ошибка product делает перенос небезопасным для выбора модели.
assert prepare.read_version(ROOT / "UKSSTMS320F28335/firmware_version.h") == [1, 0, 0] # Проверяет единственный источник начальной версии текущего продукта.
print("PASS: 137-block EEPROM dump, SPI IRQ, bounds, embedded identity, stale/truncated/oversize rejection, exact manifest and unchanged SetGUI decoder") # Подтверждает проверку выпуска и совместимость без физического обмена с платой.

123
tests/testlamp/model.inc Normal file
View File

@@ -0,0 +1,123 @@
/* Проверяет удержание TESTLAMP и выбор пакетов при уже включённых рабочих лампах. */
#include <stdio.h> /* Выводит конкретные нарушенные условия и итог сценария. */
#include <string.h> /* Обнуляет регистровое пространство перед началом проверки. */
#define interrupt /* Убирает аппаратный атрибут ISR при компиляции модели на ПК. */
typedef float FILTERBAT; /* Позволяет подключить production-заголовок с интерфейсами фильтров. */
#include "measure.h" /* Получает настоящие READY_FREQ, BLINK_TIME, CANPOWSE и maximum_bright. */
#include "package.h" /* Получает настоящие адреса TESTLAMP, яркости, ламп и типов плат. */
#include "DSP2833x_SWPrioritizedIsrLevels.h" /* Использует реальную вычисляемую маску приоритетов TI MINT13. */
typedef union { /* Моделирует слово Modbus размером хостового int, сохраняя номера битов production-псевдонимов. */
unsigned int all; /* Обеспечивает запись целого слова по тому же смещению массива. */
struct { /* Размещает шестнадцать технологических битов в младшей части слова. */
unsigned int bit0:1, bit1:1, bit2:1, bit3:1, bit4:1, bit5:1, bit6:1, bit7:1; /* Сохраняет позиции кнопок и команд 0..7. */
unsigned int bit8:1, bit9:1, bitA:1, bitB:1, bitC:1, bitD:1, bitE:1, bitF:1; /* Сохраняет позиции повторителей входов и старших команд 8..15. */
} bit; /* Предоставляет production-псевдонимам доступ к полям регистра. */
} WORDE; /* Завершает хостовое представление одного регистра Modbus. */
struct gpio_bits { /* Предоставляет входы всех ветвей настоящей get_Buttons, без моделирования электрических свойств. */
unsigned GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11; /* Представляет дискретные входы нескольких типов плат. */
unsigned GPIO20, GPIO21, GPIO22, GPIO23, GPIO24, GPIO25, GPIO26, GPIO27; /* Представляет перемычки и вход TESTLAMP GPIO26 пульта. */
unsigned GPIO48, GPIO49, GPIO50, GPIO51, GPIO52, GPIO53; /* Представляет входы банка B, читаемые get_Buttons. */
}; /* Завершает перечень моделируемых входов. */
struct gpio_reg { struct gpio_bits bit; }; /* Даёт регистрам GPADAT и GPBDAT production-форму обращения. */
static struct { struct gpio_reg GPADAT, GPBDAT; } GpioDataRegs; /* Хранит управляемые сценарием уровни входов. */
static struct { unsigned long all; } DigErr; /* Принимает диагностическую маску посторонней ветви SHKF. */
static struct { unsigned long InterruptCount; } CpuTimer1; /* Учитывает вызовы настоящего обработчика пульта. */
int modbus[129], Desk; /* Предоставляет технологические регистры и выбирает ветвь пульта. */
long INcount0; /* Повторяет тип накопителя в peripher.c проекта 166 и параметр long * функции er_anal, чтобы модель проверяла настоящий фильтр без несовместимых указателей. */
unsigned int CanPowse, CanGO; /* Хранит счётчик тактов выдачи CAN и признак готовности очередной передачи. */
unsigned long isMask, Lonely; /* Хранит тестовую маску и возраст последней связи. */
int isNumb, quaLamp = 6; /* Сохраняет тестовые цифры и число ламп из pulto.c. */
static unsigned int IER, mask_at_eint, entry_mask, failures, sends, nested_entries, timer_pending; /* Отслеживает разрешения прерываний, пакетные вызовы и возможность вложенного Timer1. */
static unsigned long lamp_word; /* Запоминает последний пакет шести сигнальных ламп канала 2. */
static long digit_word; /* Сохраняет последнее значение одного из двух семисегментных индикаторов в текущем такте. */
static int digit_dot, digit_sends; /* Запоминает режим форматирования цифр и наличие числового пакета в текущем такте. */
#define M_INT13 0x1000U /* Повторяет бит 12 INT13 из DSP2833x_Device.h для модели регистра IER. */
#define EALLOW ((void)0) /* Не моделирует защиту аппаратных регистров, поскольку она не влияет на рассматриваемые ветви. */
#define EDIS ((void)0) /* Завершает заглушку защищённой записи. */
#define EINT (mask_at_eint = IER) /* Снимает реально применённую маску в момент разрешения вложенных прерываний. */
#define ServiceDog() ((void)0) /* Исключает аппаратный сторожевой таймер из проверки индикации. */
#define toggle_ONLINE() ((void)0) /* Не моделирует электрический сигнал готовности ПЛИС. */
#define toggle_RES_OUT_1() ((void)0) /* Исключает резервный GPIO из логики выбора TESTLAMP. */
#define toggle_RES_OUT_2() ((void)0) /* Исключает второй резервный GPIO из пакетной проверки. */
void cpu_timer1_isr_PULT(void); /* Позволяет заглушке передачи смоделировать повторное аппаратное событие Timer1. */
static void kanal_Send(int adr, long dat, int dot) /* Записывает передаваемые данные, не подменяя проверяемый выбор режима. */
{ /* Начинает обработку одного production-вызова передачи. */
sends++; /* Позволяет обнаружить дополнительные вызовы передачи при повторном входе в ISR. */
if (timer_pending && (IER & M_INT13)) { /* Моделирует новый такт Timer1 во время незаконченной передачи, только когда собственное прерывание ошибочно разрешено. */
timer_pending = 0; /* Ограничивает искусственное событие одним вложенным вызовом. */
nested_entries++; /* Регистрирует перемешивание обработки двух тактов. */
cpu_timer1_isr_PULT(); /* Запускает настоящую production-функцию повторно при неверной маске. */
} /* Завершает моделирование потенциального повторного входа. */
if (adr == 2) lamp_word = (unsigned long)dat; /* Сохраняет именно канал сигнальных ламп, обновляемый в каждом такте. */
if (adr < 2) { /* Записывает также передачу цифр, чтобы обнаружить переход семисегментного индикатора в рабочий режим при провале кнопки. */
digit_word = dat; /* Сохраняет переданное production-функцией числовое значение. */
digit_dot = dot; /* Различает тест четырёх одинаковых цифр и обычное форматирование измерений. */
digit_sends++; /* Отмечает присутствие числового пакета в текущем такте. */
} /* Завершает фиксацию передачи на семисегментный индикатор. */
} /* Завершает заглушку аппаратной передачи. */
/* HANDLERS */
static void check(int condition, const char *name) /* Накапливает ошибки, позволяя проверить все последствия одного дефекта. */
{ /* Начинает регистрацию одного требования. */
if (!condition) { /* Выводит только нарушенные условия. */
printf("FAIL %s\n", name); /* Даёт мутационному тесту устойчивое имя обнаруженного дефекта. */
failures++; /* Делает код завершения ненулевым при любой регрессии. */
} /* Завершает обработку ошибки. */
} /* Завершает проверку одного требования. */
static void tick(void) /* Моделирует сохранение и восстановление IER на границе аппаратного ISR. */
{ /* Начинает один такт индикации. */
IER = entry_mask; /* Возвращает исходные разрешения источников перед новым обработчиком. */
sends = 0; /* Отделяет вызовы передачи текущего такта от предыдущих. */
digit_sends = 0; /* Не принимает пакет предыдущего такта за обновление цифр в текущем. */
cpu_timer1_isr_PULT(); /* Выполняет настоящий алгоритм выбора режима, PWM и пакетов. */
check(mask_at_eint == (entry_mask & MINT13 & ~M_INT13), "interrupt mask"); /* Проверяет отсутствие собственного Timer1 и запрет включения ранее выключенных источников. */
check(sends >= 1 && sends <= 2, "packet count"); /* Требует штатный объём одного такта: пакет ламп и не более одного пакета очередного индикатора. */
} /* Завершает один такт индикации. */
int main(void) /* Запускает на настоящих функциях сценарий рабочих ламп, теста с помехами и возврата в работу. */
{ /* Начинает общий поведенческий сценарий. */
int i, pwm_on = 0; /* Перебирает опросы и такты; считает включённые такты рабочего PWM за один полный период. */
memset(modbus, 0, sizeof(modbus)); /* Убирает старые команды и состояния перед сценарием. */
check(sizeof(WORDE) == sizeof(modbus[0]), "register layout"); /* Подтверждает корректность production-псевдонимов в хостовой модели. */
Desk = dsk_EPLT; /* Выбирает реальную ветвь чтения TESTLAMP пульта. */
modbus[2] = 1; /* Включает рабочую лампу до нажатия TESTLAMP, как в сообщении пользователя. */
Bright[0] = maximum_bright; /* Задаёт уже включённой лампе постоянную яркость для обнаружения случайных провалов. */
entry_mask = M_INT13 | (1U << 0) | (1U << 13); /* Разрешает Timer1, группу ADC и Timer2, проверяя также маскирование равного приоритета. */
tick(); /* Получает первый рабочий пакет ламп. */
check(lamp_word == 1, "normal lamp before test"); /* Подтверждает исходное постоянное свечение до нажатия. */
GpioDataRegs.GPADAT.bit.GPIO26 = 1; /* Нажимает физическую кнопку активным высоким уровнем. */
for (i = 0; i < 1001; i++) get_Buttons(); /* Проходит реальные 1000 накопительных опросов и следующий опрос фиксации порога. */
check(bTestLamp == 1, "test activation"); /* Требует включения теста после полной выдержки. */
for (i = 0; i < 600; i++) { /* Проверяет удержание более BLINK_TIME=500 тактов без подавления штатного медленного мигания. */
unsigned long expected_mask; /* Сохраняет фазу теста перед очередным возмущением кнопки. */
GpioDataRegs.GPADAT.bit.GPIO26 = 0; /* Вводит короткий провал входа на один опрос главного цикла. */
get_Buttons(); /* Выполняет настоящий накопительный фильтр и публикацию кнопки. */
check(bTestLamp == 1, "held test"); /* Проверяет сохранение TESTLAMP вместо мгновенного возврата рабочих масок. */
timer_pending = 1; /* Допускает новый аппаратный такт во время передачи для проверки запрета повторного входа. */
tick(); /* Формирует тестовый пакет при кратком провале физического входа. */
expected_mask = isMask; /* Получает ожидаемую медленную фазу из настоящей what_is. */
check(lamp_word == expected_mask, "test packet during input glitch"); /* Исключает рабочую маску уже горящих ламп из тестового потока. */
check(!digit_sends || (digit_word == isNumb && digit_dot == 4), "test digit packet during input glitch"); /* Требует сохранения тестового значения и формата цифр во время помехи кнопки. */
check(nested_entries == 0, "nested transmission"); /* Требует единственного владельца DATA/CLK на протяжении передачи. */
timer_pending = 0; /* Убирает смоделированное событие после завершения текущего такта. */
GpioDataRegs.GPADAT.bit.GPIO26 = 1; /* Возвращает уровень удерживаемой кнопки. */
get_Buttons(); /* Восстанавливает насыщенный накопитель без смены тестового режима. */
} /* Завершает серию коротких помех при удержании. */
check(isNumb == 1111 && isMask == 0xFFFFFFFFUL, "slow test sequence"); /* После 600 миллисекунд требует единственного штатного шага what_is с периодом 500 тактов, а не ускоренного строба. */
GpioDataRegs.GPADAT.bit.GPIO26 = 0; /* Отпускает физическую кнопку на длительное время. */
for (i = 0; i < 1001; i++) get_Buttons(); /* Даёт накопителю полностью разрядиться и зафиксировать отпускание. */
check(bTestLamp == 0, "test release"); /* Проверяет, что сохранение состояния не залипает в TESTLAMP. */
tick(); /* Возвращает передачу рабочей маски после отпускания. */
check(lamp_word == 1, "normal lamp after test"); /* Восстанавливает уже включённую до теста лампу. */
cTestLamp = 1; /* Включает независимую удалённую команду теста через production-регистр. */
tick(); /* Проверяет приоритет удалённого TESTLAMP при отпущенной физической кнопке. */
check(lamp_word == isMask, "remote test"); /* Требует тестовую маску для обоих источников команды. */
cTestLamp = 0; /* Завершает удалённый тест перед проверкой рабочего PWM. */
Bright[0] = 6; /* Устанавливает шесть включённых тактов из десяти в production-периоде яркости. */
for (i = 0; i < 10; i++) { /* Проверяет один полный период PWM после выхода из теста. */
tick(); /* Даёт настоящей функции вычислить текущую фазу яркости. */
check(lamp_word == 0 || lamp_word == 1, "normal PWM packet"); /* Исключает оставшиеся тестовые биты из рабочей маски. */
pwm_on += lamp_word == 1; /* Измеряет реальное число включённых тактов production-PWM. */
} /* Завершает период обычной яркости. */
check(pwm_on == 6, "normal PWM duty"); /* Подтверждает сохранение заданной яркости 6/10 после выхода из теста. */
printf("%s TESTLAMP: held input glitches, single ISR, normal lamps, release, remote command\n", failures ? "FAIL" : "PASS"); /* Выводит проверенные свойства без заявления о физическом испытании платы. */
return failures ? 1 : 0; /* Возвращает результат всех поведенческих требований. */
} /* Завершает хостовую проверку. */

View File

@@ -0,0 +1,51 @@
"""Проверяет TESTLAMP на настоящих функциях прошивки без подключения платы."""
import argparse # Позволяет использовать доступный GCC для модели аппаратного окружения.
import os # Передаёт компилятору PATH с его вспомогательными программами и библиотеками.
from pathlib import Path # Разрешает исходники и модель относительно каталога этого теста.
import re # Извлекает функции, убирая комментарии, которые могут содержать примеры скобок.
import subprocess # Компилирует и запускает независимые проверки с контролем результата.
import tempfile # Удаляет только временные исходники и исполняемые файлы данного запуска.
ROOT = Path(__file__).resolve().parents[2] # Получает корень репозитория из расположения tests/testlamp.
def source(name): # Читает production-модуль без изменения исходного файла.
data = (ROOT / "Source/Internal" / name).read_bytes() # Читает исходник без изменения его кодировки: в 166 ещё есть модули Windows-1251.
try: # Сначала использует UTF-8 для перенесённых модулей индикации.
text = data.decode("utf-8") # Строго декодирует обновлённые исходники без потери русских комментариев.
except UnicodeDecodeError: # Поддерживает старый measure.c, не изменяя его ради хостового теста.
text = data.decode("cp1251") # Читает исходную Windows-1251 только при невозможности декодирования UTF-8.
return re.sub(r"/\*.*?\*/|//[^\n]*", "", text, flags=re.S) # Убирает комментарии перед разбором вложенных блоков.
def function(text, name): # Выбирает определение функции вместо копии её алгоритма в тесте.
match = re.search(r"(?:interrupt\s+)?(?:void|int)\s+" + name + r"\([^;{}]*\)\s*\{", text) # Отличает тело функции от прототипов и вызовов.
assert match is not None, name # Завершает проверку, если проверяемая production-функция исчезла.
end, depth = match.end(), 1 # Учитывает найденную открывающую скобку внешнего блока.
while depth: # Читает все вложенные ветви до конца определения.
depth += (text[end] == "{") - (text[end] == "}") # Находит парную закрывающую скобку.
end += 1 # Включает текущий символ в извлекаемый фрагмент.
return text[match.start():end] # Сохраняет порядок операторов настоящей функции.
parser = argparse.ArgumentParser(description=__doc__) # Объясняет назначение отдельного запуска проверки.
parser.add_argument("--cc", default="C:/msys64/mingw64/bin/gcc.exe", help="Путь к GCC") # Повторяет установленный в этом проекте хостовый компилятор.
args = parser.parse_args() # Разбирает параметры до формирования временных файлов.
button = function(source("peripher.c"), "get_Buttons") # Проверяет именно публикацию отфильтрованной кнопки в Modbus.
pult = source("pulto.c") # Берёт текущую обработку индикации, включая реальные ветви теста и яркости.
body = "\n".join((function(source("measure.c"), "er_anal"), button, function(pult, "what_is"), function(pult, "cpu_timer1_isr_PULT"))) # Собирает функции, участвующие в переключении режима и передаче масок.
before, after = Path(__file__).with_name("model.inc").read_text(encoding="utf-8").split("/* HANDLERS */") # Помещает production-функции между аппаратными заглушками и поведенческими сценариями.
old_button, count_button = re.subn(r"(er_anal\(GpioDataRegs\.GPADAT\.bit\.GPIO26,\s*&INcount0,\s*1000,\s*)bTestLamp(\))", r"\g<1>0\2", body) # Возвращает сброс фильтра при одном провале только во временную мутацию.
old_mask, count_mask = re.subn(r"IER\s*&=\s*\(MINT13\s*&\s*~M_INT13\);", "IER |= MINT13;", body) # Восстанавливает ошибочное разрешение прерываний в отдельном временном варианте.
assert count_button == count_mask == 1, "Не найдены единственные места исправлений TESTLAMP" # Не позволяет мутации незаметно проверять неизменённый код.
environment = dict(os.environ) # Сохраняет окружение основного процесса.
environment["PATH"] = str(Path(args.cc).resolve().parent) + os.pathsep + environment.get("PATH", "") # Делает DLL и вспомогательные программы GCC доступными дочерним процессам.
with tempfile.TemporaryDirectory(prefix="testlamp-") as directory: # Изолирует артефакты каждой проверки от дерева прошивки.
for name, functions, failure in (("production", body, None), ("old_button", old_button, "FAIL held test"), ("old_mask", old_mask, "FAIL interrupt mask")): # Проверяет исправление и чувствительность сценариев к каждой из двух старых ошибок.
c_path, exe_path = Path(directory) / (name + ".c"), Path(directory) / (name + ".exe") # Разделяет временные исходники и программы трёх вариантов.
c_path.write_text(before + functions + after, encoding="utf-8") # Сохраняет модель и русские пояснения в UTF-8.
subprocess.run([args.cc, "-std=c99", "-O3", "-fno-strict-aliasing", "-Werror=implicit-function-declaration", "-I", str(ROOT / "Source/Internal/Include"), "-I", str(ROOT / "Source/External/v120/DSP2833x_common/include"), str(c_path), "-o", str(exe_path)], env=environment, check=True) # Сохраняет адреса production-псевдонимов регистров и использует реальную таблицу приоритетов TI.
result = subprocess.run([str(exe_path)], env=environment, capture_output=True, text=True, timeout=20) # Ограничивает зависание и сохраняет конкретные нарушенные условия.
if failure is None: # Рабочая версия обязана пройти все сценарии.
print(result.stdout, end="") # Показывает результат поведенческих проверок текущей прошивки.
assert result.returncode == 0, result.stdout + result.stderr # Возвращает ошибку при любой регрессии TESTLAMP или рабочего режима.
else: # Старая ошибка должна воспроизводимо нарушить соответствующее требование.
assert result.returncode == 1 and failure in result.stdout, result.stdout + result.stderr # Доказывает, что тест действительно обнаруживает возвращённый дефект.
print("PASS mutation:", name, "rejected") # Сообщает об ожидаемом провале временного варианта со старой ошибкой.

View File

@@ -1,6 +1,5 @@
#include "../../Source/Internal/log_to_mem.c"
#include "../../Source/Internal/Include/ump_can_protocol.h"
uint16_t firmware_info_static_read(unsigned index) { (void)index; return 0U; } /* Изолирует проверку старого логгера от данных сборки; реальные слова паспорта проверяются отдельным port_test.c. */
struct mock_gpio GpioDataRegs;
int READY = 0, Desk = 6, Mode = 5;
int modbus[128];
@@ -14,76 +13,37 @@ unsigned int adc_table_lem[4];
__declspec(dllexport) int run_tests(void)
{
unsigned int i, generation, archived, reply[UMP_CAN_MAX_WORDS], error, length;
/* Индексы перебирают все поля десяти записей онлайн-окна, проверяя отсутствие сдвига на старые 40 слов. */
unsigned int record_index, word_index, block_words;
unsigned long sequence;
READY = 100;
CHECK(!ump_log_write(UMP_LOG_BASE, 1));
READY = 0;
/* Явная инициализация кэша гарантирует нулевые ЗПТ до первого принятого измерения. */
CHECK(ump_pm67_voltage[0] == 0 && ump_pm67_voltage[1] == 0);
/* Регистр 306 ПМ67 передаётся нулевым CAN-индексом 305 в первой позиции кадра. */
ump_log_can(0x8000U | 305U, 321, 0, 0);
/* Регистр 9 ПМ67 находится во второй позиции кадра с начальным индексом 7 и маской 0x4000. */
ump_log_can(0x4000U | 7U, 0, 456, 0);
/* Напряжения должны обновляться ещё до включения регистратора, чтобы первый снимок уже содержал измерения. */
CHECK(ump_pm67_voltage[0] == 321 && ump_pm67_voltage[1] == 456 && ump_count == 0);
/* Нулевая маска не разрешает первое слово и не должна подменять сохранённое напряжение. */
ump_log_can(305U, 999, 0, 0);
/* Индекс 306 не является индексом регистра 306: прошивка принимает именно нулевую нумерацию CAN. */
ump_log_can(0x8000U | 306U, 999, 0, 0);
/* Оба неверных заголовка обязаны оставить последнее корректное ЗПТ1 без изменения. */
CHECK(ump_pm67_voltage[0] == 321);
/* Третья позиция соответствует индексу 305 даже при пропущенных первых двух словах кадра. */
ump_log_can(0x2000U | 303U, 0, 0, 123);
/* Аналогично проверяет третью позицию для нулевого индекса 8 второго напряжения. */
ump_log_can(0x2000U | 6U, 0, 0, 789);
/* Последний принятый набор должен совпадать с ожидаемыми ЗПТ1/ЗПТ2 в вольтах. */
CHECK(ump_pm67_voltage[0] == 123 && ump_pm67_voltage[1] == 789);
adc_table_lem[0] = 2048; adc_table_lem[3] = 4095;
CHECK(!ump_log_write(UMP_LOG_BASE+1, 0));
CHECK(!ump_log_write(UMP_LOG_BASE+1, 1001));
CHECK(ump_log_write(UMP_LOG_BASE+1, 1));
/* Дополнительные биты полного слова команд не должны попадать в компактное поле пуска/сброса v4. */
modbus[127] = 0xAB80;
modbus[127] = 0x80;
/* Unrelated registers must not determine the fixed 166 ramp. */
modbus[0x50] = 99; modbus[0x51] = 123;
modbus[0x64] = 88; modbus[0x65] = 111;
modbus[0x7B] = 1234; modbus[0x1C] = 160;
/* Сторожевое значение во втором слоте обнаружит случайную запись старых полей за 12-словную границу. */
logs_block[UMP_LOG_WORDS] = 0xA55A;
/* Второй банк не должен затрагиваться заполнением и оборотом кольца первого банка. */
logs_block[UMP_LOG_CAPACITY * UMP_LOG_WORDS] = 0x5AA5;
CHECK(ump_log_write(UMP_LOG_BASE, 1));
/* Число слов и чётная длина совпадают с компактным форматом исполнения 167. */
CHECK(UMP_LOG_WORDS == 12 && 2UL * UMP_LOG_CAPACITY * UMP_LOG_WORDS <= LOG_PAGE_LEN);
/* Первый снимок обязан содержать напряжения, принятые до запуска записи. */
CHECK(ump_word(0, 0, 9) == 123 && ump_word(0, 0, 10) == 789);
/* Нулевой резерв необходим для парной упаковки CAN и не должен содержать старый код ЦАП. */
CHECK(ump_word(0, 0, 11) == 0 && logs_block[UMP_LOG_WORDS] == 0xA55A);
/* В начале доступен один снимок, а оставшаяся часть 124-словного онлайн-окна заполнена нулями. */
CHECK(ump_log_read(UMP_LOG_LIVE+2) == 1 && ump_log_read(UMP_LOG_LIVE+4+UMP_LOG_WORDS) == 0);
CHECK(!ump_log_write(UMP_LOG_BASE, 1));
CHECK(!ump_log_write(UMP_LOG_BASE+1, 20));
for (i=0; i<900; i++) ump_log_tick();
CHECK(ump_count == UMP_LOG_CAPACITY);
CHECK(ump_word(0, 0, 0) == 101);
CHECK(ump_word(0, UMP_LOG_CAPACITY-1, 0) == 900);
/* После оборота кольца поле команд содержит только биты пуска и сброса, несмотря на посторонние биты 0xAB80. */
CHECK(ump_word(0, 0, 3) == 0x80);
/* Расчётное задание тока переместилось в слово 6 и сохраняет масштаб 0,1 мА. */
CHECK(ump_word(0, 0, 6) == 160);
/* Уставки исполнения 166 остаются фиксированными 6/16 мА независимо от технологических регистров 167. */
CHECK(ump_word(0, 0, 7) == 6 && ump_word(0, 0, 8) == 16);
/* При обороте кольца не теряются оба напряжения и не изменяется сторожевое значение следующего банка. */
CHECK(ump_word(0, 0, 9) == 123 && ump_word(0, 0, 10) == 789 && logs_block[UMP_LOG_CAPACITY * UMP_LOG_WORDS] == 0x5AA5);
CHECK(ump_word(0, 0, 8) == 1234);
CHECK(ump_word(0, 0, 24) == 6);
CHECK(ump_word(0, 0, 25) == 16);
CHECK(ump_word(0, 0, 36) == 2048);
CHECK(ump_word(0, 0, 39) == 4095);
/* Короткая CAN-команда должна остаться отдельным событием. */
modbus[127] = 0x40;
ump_log_can(0x807F, 0x40, 0, 0);
CHECK(ump_post == 1000);
CHECK(ump_word(0, ump_count-1, 2) == 0x102);
/* Событие CAN хранит выбранные команды в слове 3 вместо удалённого сырого заголовка в прежнем слове 18. */
CHECK(ump_word(0, ump_count-1, 3) == 0x40);
CHECK(ump_word(0, ump_count-1, 18) == 0x807F);
for (i=0; i<999; i++) ump_log_tick();
CHECK(ump_generation == 0);
ump_log_tick();
@@ -99,16 +59,8 @@ __declspec(dllexport) int run_tests(void)
CHECK(ump_word(1, UMP_LOG_CAPACITY-1, 0) == archived);
CHECK(ump_generation == generation);
CHECK(ump_sequence == sequence + 200);
/* Уменьшение записи увеличивает глубину онлайн-окна с трёх до десяти полных снимков. */
CHECK(ump_log_read(UMP_LOG_LIVE+2) == UMP_LOG_LIVE_RECORDS && UMP_LOG_LIVE_RECORDS == 10);
/* Первое онлайн-время относится к самому старому из десяти переданных снимков. */
CHECK(ump_log_read(UMP_LOG_LIVE+4) == ump_time-UMP_LOG_LIVE_RECORDS+1);
/* Сопоставляет каждую запись онлайн-окна с хронологическим чтением кольцевого банка. */
for (record_index = 0; record_index < UMP_LOG_LIVE_RECORDS; record_index++)
/* Проверяет все 12 слов, включая время, выбранные сигналы и нулевой резерв. */
for (word_index = 0; word_index < UMP_LOG_WORDS; word_index++)
/* Смещения должны заполнять ровно 120 полезных слов после четырёх служебных слов. */
CHECK(ump_log_read(UMP_LOG_LIVE+4+record_index*UMP_LOG_WORDS+word_index) == ump_word(0, ump_count-UMP_LOG_LIVE_RECORDS+record_index, word_index));
CHECK(ump_log_read(UMP_LOG_LIVE+2) == 3);
CHECK(ump_log_read(UMP_LOG_LIVE+4) == ump_time-2);
CHECK(ump_log_write(UMP_LOG_BASE+3, 1));
CHECK(ump_log_write(UMP_LOG_BASE+2, UMP_LOG_CAPACITY-1));
CHECK(ump_log_read(UMP_LOG_DATA) == archived);
@@ -129,27 +81,7 @@ __declspec(dllexport) int run_tests(void)
/* CAN читает те же регистры и формирует цельный снимок из 124 слов.
* Запись по старым технологическим адресам через сервис запрещена. */
length = ump_can_prepare(3, UMP_LOG_BASE, 16, reply, &error);
/* Служебный ответ должен объявлять версию v4, длину 12 слов и сохранённую ёмкость банка исполнения 166. */
CHECK(!error && length == 16 && reply[0] == 0x554D && reply[1] == 4 && reply[4] == 12 && reply[5] == UMP_LOG_CAPACITY);
/* Проверяет каждое разрешённое число целых компактных записей в архивном CAN-окне. */
for (block_words = UMP_LOG_WORDS; block_words <= 120; block_words += UMP_LOG_WORDS) {
/* Запрашивает от одной до десяти записей, не превышая полезную длину 120 слов. */
length = ump_can_prepare(3, UMP_LOG_DATA, block_words, reply, &error);
/* Все кратные длине записи запросы должны проходить без исключения Modbus. */
CHECK(!error && length == block_words);
} /* Завершает проверку допустимых размеров окна компактного архива. */
/* Прежний блок v2 из 40 слов теперь обрывает запись v4 и должен отклоняться CAN-протоколом. */
ump_can_prepare(3, UMP_LOG_DATA, 40, reply, &error);
/* Исключение 2 сообщает о недопустимом диапазоне регистров. */
CHECK(error == 2);
/* Отклоняет пустой архивный запрос вместо его трактовки как успешного чтения. */
ump_can_prepare(3, UMP_LOG_DATA, 0, reply, &error);
/* Нулевая длина не содержит ни одной целой записи v4. */
CHECK(error == 2);
/* Блок из 11 записей уже превышает разрешённые 120 слов полезного окна. */
ump_can_prepare(3, UMP_LOG_DATA, 132, reply, &error);
/* Ограничение защищает буфер ответа от записи за его границами. */
CHECK(error == 2);
CHECK(!error && length == 16 && reply[0] == 0x554D && reply[1] == 2);
length = ump_can_prepare(3, UMP_LOG_LIVE, 124, reply, &error);
CHECK(!error && length == 124);
ump_can_prepare(3, UMP_LOG_LIVE, 126, reply, &error);

View File

@@ -11,24 +11,21 @@ import subprocess
import tempfile
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--compiler', default=shutil.which('clang') or shutil.which('cl') or shutil.which('gcc') or ('C:/msys64/mingw64/bin/gcc.exe' if Path('C:/msys64/mingw64/bin/gcc.exe').is_file() else None)) # Поддерживает имеющийся GCC для проверки нового include-пути без обязательной установки другого host-компилятора.
parser.add_argument('--compiler', default=shutil.which('clang') or shutil.which('cl'))
args = parser.parse_args()
if not args.compiler:
parser.error('Provide --compiler pointing to clang.exe, cl.exe or gcc.exe') # Явно перечисляет поддерживаемые инструменты проверки, не меняя компилятор самой прошивки.
parser.error('Provide --compiler pointing to clang.exe or cl.exe')
root = Path(__file__).resolve().parents[2]
with tempfile.TemporaryDirectory(prefix='ump-recorder-') as directory:
output = Path(directory)
library = output / 'recorder_test.dll'
includes = [root / 'tests/ump_logger/stubs', root / 'Source/Internal/Include', root.parent / 'newProject/templates/c/firmware-info/include'] # Адаптер CAN теперь использует общий статический интерфейс паспорта, как production-сборка 166.
includes = [root / 'tests/ump_logger/stubs', root / 'Source/Internal/Include']
source = root / 'tests/ump_logger/recorder_test.c'
if Path(args.compiler).stem.lower() == 'cl':
command = [args.compiler, '/nologo', '/LD', '/GS-', '/wd4068', '/wd4312',
*['/I' + str(p) for p in includes], str(source),
'/Fo' + str(output / 'recorder.obj'), '/link', '/NOENTRY',
'/NODEFAULTLIB', '/OUT:' + str(library)]
elif Path(args.compiler).stem.lower() in ('gcc', 'cc'): # MinGW использует обычную DLL с CRT и не принимает специальные параметры компоновщика Clang/lld.
command = [args.compiler, '-shared', '-O3', '-Wno-unknown-pragmas', '-Wno-int-to-pointer-cast', *['-I' + str(p) for p in includes], str(source), '-o', str(library)] # Компилирует тот же существующий recorder_test.c и production-логгер без копирования его алгоритма.
os.environ['PATH'] = str(Path(args.compiler).resolve().parent) + os.pathsep + os.environ.get('PATH', '') # Даёт дочернему Python доступ к runtime DLL выбранного MinGW только в этом тестовом процессе.
else:
command = [args.compiler, '-shared', '-nostdlib', '-fuse-ld=lld',
'-Wl,/noentry,/nodefaultlib', '-Wno-unknown-pragmas',