5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "modelslist.h"
24 ModelsList modelslist
;
26 ModelCell::ModelCell(const char * name
)
27 : buffer(NULL
), valid_rfData(false)
29 strncpy(modelFilename
, name
, sizeof(modelFilename
));
30 memset(modelName
, 0, sizeof(modelName
));
33 ModelCell::~ModelCell()
38 void ModelCell::setModelName(char* name
)
40 zchar2str(modelName
, name
, LEN_MODEL_NAME
);
41 if (modelName
[0] == 0) {
43 strncpy(modelName
, modelFilename
, LEN_MODEL_NAME
);
44 tmp
= (char *) memchr(modelName
, '.', LEN_MODEL_NAME
);
52 void ModelCell::setModelId(uint8_t moduleIdx
, uint8_t id
)
54 modelId
[moduleIdx
] = id
;
57 void ModelCell::resetBuffer()
65 const BitmapBuffer
* ModelCell::getBuffer()
73 void ModelCell::loadBitmap()
77 TimerData timers
[MAX_TIMERS
];
79 const char * error
= NULL
;
81 buffer
= new BitmapBuffer(BMP_RGB565
, MODELCELL_WIDTH
, MODELCELL_HEIGHT
);
86 if (strncmp(modelFilename
, g_eeGeneral
.currModelFilename
, LEN_MODEL_FILENAME
) == 0) {
87 memcpy(&partialmodel
.header
, &g_model
.header
, sizeof(partialmodel
));
90 error
= readModel(modelFilename
, (uint8_t *)&partialmodel
.header
, sizeof(partialmodel
));
93 buffer
->clear(TEXT_BGCOLOR
);
96 buffer
->drawText(5, 2, "(Invalid Model)", TEXT_COLOR
);
97 buffer
->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT
, TEXT_COLOR
);
100 if (modelName
[0] == 0)
101 setModelName(partialmodel
.header
.name
);
103 char timer
[LEN_TIMER_STRING
];
104 buffer
->drawSizedText(5, 2, modelName
, LEN_MODEL_NAME
, SMLSIZE
|TEXT_COLOR
);
105 getTimerString(timer
, 0);
106 for (uint8_t i
= 0; i
< MAX_TIMERS
; i
++) {
107 if (partialmodel
.timers
[i
].mode
> 0 && partialmodel
.timers
[i
].persistent
) {
108 getTimerString(timer
, partialmodel
.timers
[i
].value
);
112 buffer
->drawText(101, 40, timer
, TEXT_COLOR
);
113 for (int i
=0; i
<4; i
++) {
114 buffer
->drawBitmapPattern(104+i
*11, 25, LBM_SCORE0
, TITLE_BGCOLOR
);
116 GET_FILENAME(filename
, BITMAPS_PATH
, partialmodel
.header
.bitmap
, "");
117 const BitmapBuffer
* bitmap
= BitmapBuffer::load(filename
);
119 buffer
->drawScaledBitmap(bitmap
, 5, 24, 56, 32);
123 buffer
->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT
, TEXT_COLOR
);
126 buffer
->drawSolidHorizontalLine(5, 19, 143, LINE_COLOR
);
129 void ModelCell::save(FIL
* file
)
131 f_puts(modelFilename
, file
);
135 void ModelCell::setRfData(ModelData
* model
)
137 for (uint8_t i
= 0; i
< NUM_MODULES
; i
++) {
138 modelId
[i
] = model
->header
.modelId
[i
];
139 setRfModuleData(i
, &(model
->moduleData
[i
]));
140 TRACE("<%s/%i> : %X,%X,%X",
141 strlen(modelName
) ? modelName
: modelFilename
,
142 i
, moduleData
[i
].type
, moduleData
[i
].rfProtocol
, modelId
[i
]);
147 void ModelCell::setRfModuleData(uint8_t moduleIdx
, ModuleData
* modData
)
149 moduleData
[moduleIdx
].type
= modData
->type
;
150 if (modData
->type
!= MODULE_TYPE_MULTIMODULE
) {
151 moduleData
[moduleIdx
].rfProtocol
= (uint8_t)modData
->rfProtocol
;
154 // do we care here about MM_RF_CUSTOM_SELECTED? probably not...
155 moduleData
[moduleIdx
].rfProtocol
= modData
->getMultiProtocol(false);
159 bool ModelCell::fetchRfData()
161 //TODO: use g_model in case fetching data for current model
164 getModelPath(buf
, modelFilename
);
169 const char* err
= openFile(buf
,&file
,&file_size
);
170 if (err
) return false;
172 FSIZE_t start_offset
= f_tell(&file
);
176 if ((f_read(&file
, buf
, LEN_MODEL_NAME
, &read
) != FR_OK
) || (read
!= LEN_MODEL_NAME
))
181 // 1. fetch modelId: NUM_MODULES @ offsetof(ModelHeader, modelId)
182 // if (f_lseek(&file, start_offset + offsetof(ModelHeader, modelId)) != FR_OK)
184 if ((f_read(&file
, modelId
, NUM_MODULES
, &read
) != FR_OK
) || (read
!= NUM_MODULES
))
187 // 2. fetch ModuleData: sizeof(ModuleData)*NUM_MODULES @ offsetof(ModelData, moduleData)
188 if (f_lseek(&file
, start_offset
+ offsetof(ModelData
, moduleData
)) != FR_OK
)
191 for(uint8_t i
=0; i
<NUM_MODULES
; i
++) {
193 if ((f_read(&file
, &modData
, NUM_MODULES
, &read
) != FR_OK
) || (read
!= NUM_MODULES
))
196 setRfModuleData(i
, &modData
);
208 ModelsCategory::ModelsCategory(const char * name
)
210 strncpy(this->name
, name
, sizeof(this->name
));
213 ModelsCategory::~ModelsCategory()
215 for (list
<ModelCell
*>::iterator it
= begin(); it
!= end(); ++it
) {
221 ModelCell
* ModelsCategory::addModel(const char * name
)
223 ModelCell
* result
= new ModelCell(name
);
228 void ModelsCategory::removeModel(ModelCell
* model
)
234 void ModelsCategory::moveModel(ModelCell
* model
, int8_t step
)
236 ModelsCategory::iterator current
= begin();
237 for (; current
!= end(); current
++) {
238 if (*current
== model
) {
243 ModelsCategory::iterator new_position
= current
;
245 while (step
>= 0 && new_position
!= end()) {
251 while (step
< 0 && new_position
!= begin()) {
257 insert(new_position
, 1, *current
);
261 void ModelsCategory::save(FIL
* file
)
267 for (list
<ModelCell
*>::iterator it
= begin(); it
!= end(); ++it
) {
272 ModelsList::ModelsList()
277 ModelsList::~ModelsList()
282 void ModelsList::init()
285 currentCategory
= NULL
;
290 void ModelsList::clear()
292 for (list
<ModelsCategory
*>::iterator it
= categories
.begin(); it
!= categories
.end(); ++it
) {
299 bool ModelsList::load()
301 char line
[LEN_MODELS_IDX_LINE
+1];
302 ModelsCategory
* category
= NULL
;
307 FRESULT result
= f_open(&file
, RADIO_MODELSLIST_PATH
, FA_OPEN_EXISTING
| FA_READ
);
308 if (result
== FR_OK
) {
309 while (readNextLine(line
, LEN_MODELS_IDX_LINE
)) {
310 int len
= strlen(line
); // TODO could be returned by readNextLine
311 if (len
> 2 && line
[0] == '[' && line
[len
-1] == ']') {
313 category
= new ModelsCategory(&line
[1]);
314 categories
.push_back(category
);
318 //char* rf_data_str = cutModelFilename(line);
319 ModelCell
* model
= new ModelCell(line
);
321 category
= new ModelsCategory("Models");
322 categories
.push_back(category
);
324 category
->push_back(model
);
325 if (!strncmp(line
, g_eeGeneral
.currModelFilename
, LEN_MODEL_FILENAME
)) {
326 currentCategory
= category
;
327 currentModel
= model
;
329 //parseModulesData(model, rf_data_str);
330 //TRACE("model=<%s>, valid_rfData=<%i>",model->modelFilename,model->valid_rfData);
331 model
->fetchRfData();
337 if (!getCurrentModel()) {
338 TRACE("currentModel is NULL");
342 if (categories
.size() == 0) {
343 category
= new ModelsCategory("Models");
344 categories
.push_back(category
);
351 void ModelsList::save()
353 FRESULT result
= f_open(&file
, RADIO_MODELSLIST_PATH
, FA_CREATE_ALWAYS
| FA_WRITE
);
354 if (result
!= FR_OK
) {
358 for (list
<ModelsCategory
*>::iterator it
= categories
.begin(); it
!= categories
.end(); ++it
) {
365 void ModelsList::setCurrentCategorie(ModelsCategory
* cat
)
367 currentCategory
= cat
;
370 void ModelsList::setCurrentModel(ModelCell
* cell
)
373 if (!currentModel
->valid_rfData
)
374 currentModel
->fetchRfData();
377 bool ModelsList::readNextLine(char * line
, int maxlen
)
379 if (f_gets(line
, maxlen
, &file
) != NULL
) {
380 int curlen
= strlen(line
) - 1;
381 if (line
[curlen
] == '\n') { // remove unwanted chars if file was edited using windows
382 if (line
[curlen
- 1] == '\r') {
383 line
[curlen
- 1] = 0;
394 ModelsCategory
* ModelsList::createCategory()
396 ModelsCategory
* result
= new ModelsCategory("Category");
397 categories
.push_back(result
);
402 ModelCell
* ModelsList::addModel(ModelsCategory
* category
, const char * name
)
404 ModelCell
* result
= category
->addModel(name
);
410 void ModelsList::removeCategory(ModelsCategory
* category
)
412 modelsCount
-= category
->size();
414 categories
.remove(category
);
417 void ModelsList::removeModel(ModelsCategory
* category
, ModelCell
* model
)
419 category
->removeModel(model
);
424 void ModelsList::moveModel(ModelsCategory
* category
, ModelCell
* model
, int8_t step
)
426 category
->moveModel(model
, step
);
430 void ModelsList::moveModel(ModelCell
* model
, ModelsCategory
* previous_category
, ModelsCategory
* new_category
)
432 previous_category
->remove(model
);
433 new_category
->push_back(model
);
437 bool ModelsList::isModelIdUnique(uint8_t moduleIdx
, char* warn_buf
, size_t warn_buf_len
)
439 ModelCell
* mod_cell
= modelslist
.getCurrentModel();
440 if (!mod_cell
|| !mod_cell
->valid_rfData
) {
441 // in doubt, pretend it's unique
445 uint8_t modelId
= mod_cell
->modelId
[moduleIdx
];
446 uint8_t type
= mod_cell
->moduleData
[moduleIdx
].type
;
447 uint8_t rfProtocol
= mod_cell
->moduleData
[moduleIdx
].rfProtocol
;
449 uint8_t additionalOnes
= 0;
450 char* curr
= warn_buf
;
453 bool hit_found
= false;
454 const std::list
<ModelsCategory
*>& cats
= modelslist
.getCategories();
455 std::list
<ModelsCategory
*>::const_iterator cat_it
= cats
.begin();
456 for (;cat_it
!= cats
.end(); cat_it
++) {
457 for (ModelsCategory::const_iterator it
= (*cat_it
)->begin(); it
!= (*cat_it
)->end(); it
++) {
461 if (!(*it
)->valid_rfData
)
464 if ((type
!= MODULE_TYPE_NONE
) &&
465 (type
== (*it
)->moduleData
[moduleIdx
].type
) &&
466 (rfProtocol
== (*it
)->moduleData
[moduleIdx
].rfProtocol
) &&
467 (modelId
== (*it
)->modelId
[moduleIdx
])) {
472 const char* modelName
= (*it
)->modelName
;
473 const char* modelFilename
= (*it
)->modelFilename
;
475 // you cannot rely exactly on WARNING_LINE_LEN so using WARNING_LINE_LEN-2 (-2 for the ",")
476 if ((warn_buf_len
- 2 - (curr
- warn_buf
)) > LEN_MODEL_NAME
) {
477 if (warn_buf
[0] != 0)
478 curr
= strAppend(curr
, ", ");
479 if (modelName
[0] == 0) {
480 size_t len
= min
<size_t>(strlen(modelFilename
),LEN_MODEL_NAME
);
481 curr
= strAppendFilename(curr
, modelFilename
, len
);
484 curr
= strAppend(curr
, modelName
, LEN_MODEL_NAME
);
493 if (additionalOnes
&& (warn_buf_len
- (curr
- warn_buf
) >= 7)) {
494 curr
= strAppend(curr
, " (+");
495 curr
= strAppendUnsigned(curr
, additionalOnes
);
496 curr
= strAppend(curr
, ")");
502 uint8_t ModelsList::findNextUnusedModelId(uint8_t moduleIdx
)
504 ModelCell
* mod_cell
= modelslist
.getCurrentModel();
505 if (!mod_cell
|| !mod_cell
->valid_rfData
) {
509 uint8_t type
= mod_cell
->moduleData
[moduleIdx
].type
;
510 uint8_t rfProtocol
= mod_cell
->moduleData
[moduleIdx
].rfProtocol
;
512 // assume 63 is the highest Model ID
514 uint8_t usedModelIds
[8];
515 memset(usedModelIds
, 0, sizeof(usedModelIds
));
517 const std::list
<ModelsCategory
*>& cats
= modelslist
.getCategories();
518 std::list
<ModelsCategory
*>::const_iterator cat_it
= cats
.begin();
519 for (;cat_it
!= cats
.end(); cat_it
++) {
520 for (ModelsCategory::const_iterator it
= (*cat_it
)->begin(); it
!= (*cat_it
)->end(); it
++) {
524 if (!(*it
)->valid_rfData
)
527 // match module type and RF protocol
528 if ((type
!= MODULE_TYPE_NONE
) &&
529 (type
== (*it
)->moduleData
[moduleIdx
].type
) &&
530 (rfProtocol
== (*it
)->moduleData
[moduleIdx
].rfProtocol
)) {
532 uint8_t id
= (*it
)->modelId
[moduleIdx
];
535 for (uint8_t i
= 1; i
< (id
& 7); i
++)
538 usedModelIds
[id
>> 3] |= mask
;
544 uint8_t tst_mask
= 1;
545 for (;new_id
< MAX_RX_NUM(moduleIdx
); new_id
++) {
546 if (!(usedModelIds
[new_id
>> 3] & tst_mask
)) {
550 if ((tst_mask
<<= 1) == 0)
554 // failed finding something...
558 void ModelsList::onNewModelCreated(ModelCell
* cell
, ModelData
* model
)
560 cell
->setModelName(model
->header
.name
);
561 cell
->setRfData(model
);
563 uint8_t new_id
= findNextUnusedModelId(INTERNAL_MODULE
);
564 model
->header
.modelId
[INTERNAL_MODULE
] = new_id
;
565 cell
->setModelId(INTERNAL_MODULE
, new_id
);