Русский
Русский
English
Статистика
Реклама

Использование xAPI (Tin Can) и CMI5 в имитаторах

image
Несмотря на то, что SCORM 2004 еще держит позиции, пора начинать поддерживать новые стандарты. Сегодня попробуем разобраться с xAPI / TinCab / CMI5. Обязательно протестируем код на официальных сайтах www.SCORM.com и www.adlnet.gov.

Итак, Tin Can API это спецификация программ в сфере дистанционного обучения, которая позволяет обучающим системам общаться между собой путём отслеживания и записи учебных занятий всех видов. Информация об учебной деятельности сохраняется в специальную базу хранилище учебных записей (англ. learning record store, LRS).

Особенности Tin Can API:

Tin Can API улучшенная версия спецификации SCORM
Tin Can API позволяет записывать любой опыт обучения, что дает нам более полную картину обучения конкретного человека
Tin Can API снимает с данных ограничения, накладываемые СДО
Tin Can API способен оказать неоценимую помощь учебным отделам, сопоставляя данные о качестве выполнения работы с учебными данными, тем самым повышая эффективность обучения.

Это теория, теперь практика.

При работе с SCORM 2000 все было относительно просто, нужно было выставить значения фиксированных переменных или получить значения фиксированных переменных.



Ну например

min = 0
max= 100
raw_score = 100
scaled = raw_score / max -- Оценка приведенная к диапазону 0..1.

ScormSetValue("cmi.score.min", ""..min); -- Минимальная оценка
ScormSetValue("cmi.score.max", ""..max); -- Максимальная оценка
ScormSetValue("cmi.score.raw", ""..raw_score); -- Полученная оценка
ScormSetValue("cmi.score.scaled", ""..scaled); -- Оценка приведенная к диапазону 0..1.

--Объем (количество) 0..1
ScormSetValue("cmi.progress_measure", "1");

ScormSetValue("cmi.success_status", "passed");
ScormSetValue("cmi.completion_status", "completed");

ScormGetValue("cmi.learner_name");
ScormGetValue("cmi.learner_id");
ScormGetValue("cmi.suspend_data");
ScormGetValue("cmi.scaled_passing_score");
ScormGetValue("cmi.completion_threshold");

print ( ScormGetValue("cmi._version"))
print ( ScormGetValue("cmi.total_time"))
print ( ScormGetValue("cmi.time_limit_action"))
print ( ScormGetValue("cmi.max_time_allowed"))

--Запись значений интеракций
ScormSetValue("cmi.interactions.0.id","Step1");
ScormSetValue("cmi.interactions.0.description", "17:14:28 Произвести аварийный останов работающих компрессорных станций")
ScormSetValue("cmi.interactions.0.result","correct");

ScormSetValue("cmi.interactions.1.id","Step2");
ScormSetValue("cmi.interactions.1.type","fill-in");
ScormSetValue("cmi.interactions.1.objectives.0.id","urn:ADL:objectiveid-0001");
ScormSetValue("cmi.interactions.1.description", "privet");
ScormSetValue("cmi.interactions.1.learner_response", "privet");
ScormSetValue("cmi.interactions.1.timestamp", "2005-10-11T09:00:30");
ScormSetValue("cmi.interactions.1.correct_responses.0.pattern", "privet");
ScormSetValue("cmi.interactions.1.weighting", "1");
--correct, incorrect, unanticipated, neutral , number 0..1
ScormSetValue("cmi.interactions.1.result","unanticipated");
ScormSetValue("cmi.interactions.1.latency", "PT0H0M5.0S");

ScormSetValue ("cmi.comments_from_learner.0.comment",q1);
ScormSetValue ("cmi.comments_from_learner.1.comment",q2);


Примерно так все и делалось Теперь на xAPI

Далее идет список тех LRS на которых я выполнял тестирование взаимодействия (необходима регистрация и получение login/pass соответственно)



Для взаимодействия с xAPI на C++ нам понадобится CURL и какая-нибудь библиотека для работы с JSON (cJSON например)

Тогда использование xAPI можно выполнять например так:

TinCanAddRecord(actor:::mbox:::mailto:mathmodel@mathmodel.com) TinCanAddRecord(actor:::name:::mathmodel)
TinCanAddRecord(actor:::objectType:::Agent)
TinCanAddRecord(verb:::id:::http://adlnet.gov/expapi/verbs/interacted)
TinCanAddRecord(object:::id:::http://lcontent.ru/lms1/simulator2)
TinCanAddRecord(object:::objectType:::Activity)

TinCanAddRecord(object:::definition:::type:::http://www.lcontent.ru/lms1/simulator1)
TinCanAddRecord(object:::definition:::name:::en-US:::mathmodel)
TinCanAddRecord(object:::definition:::description:::en-US:::mathmodel log)

TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/Teapot1 angle::: a1)
TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/Teapot2 angle::: a2)
TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/Teapot3 angle::: a3)
TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/time::: (os.clock() veryoldtime))

TinCanAddRecord(actor:::mbox:::mailto:maxgammer@gmail.com)
TinCanAddRecord(actor:::name:::Maxim Gammer)
TinCanAddRecord(actor:::objectType:::Agent)
TinCanAddRecord(verb:::id:::http://adlnet.gov/expapi/verbs/interacted)
TinCanAddRecord(object:::id:::http://lcontent.ru/lms1/simulator2)
TinCanAddRecord(object:::objectType:::Activity)
TinCanAddRecord(object:::definition:::type:::http://lcontent.ru/lms1/simulator1)
TinCanAddRecord(object:::definition:::name:::en-US:::User move)
TinCanAddRecord(object:::definition:::description:::en-US:::User coordinates)
TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/time::: (os.clock() veryoldtime))

TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/X::: UserData.X)
TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/Y::: UserData.Y)
TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/Z::: UserData.Z)
TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/HeadYaw::: UserData.HeadYaw)
TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/HeadPitch::: UserData.HeadPitch)
TinCanAddRecord(object:::extensions:::http://lcontent.ru/upsv/HeadRoll::: UserData.HeadRoll)

Все, смотрим записи в LRS

"

InterfaceForTinCan.cpp
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include #include <string.h>
#include #include #include #include #include #include #include <curl/curl.h>

//using namespace std;

#ifdef WIN32
#include "./cJSON.h"
#else
#include "./cJSON.h"
#endif

class InterfaceForTinCan
{
public:
InterfaceForTinCan();
void AddTinCanRecord(std::string str, std::string type);
void PostToLRS(std::string host, std::string login, std::string password);
void PostFileToLRS(std::string filename);
void PostToFile (std::string filename);


private:
std::vector<std::string> split(const std::string& s, const std::string& delim, const bool keep_empty = true ) ;

//созданные объекты
std::map <std::string, cJSON *> OBJECTS;
//
cJSON *top;

std::string LRS_host;
std::string LRS_login;
std::string LRS_password;

void PostStringToLRS(std::string zzz);

};

InterfaceForTinCan::InterfaceForTinCan()
{
top=cJSON_CreateObject();
}

void InterfaceForTinCan::AddTinCanRecord(std::string str, std::string type)
{
//1. преобразуем страку в вектор через разделитель (::: или @@@)
std::vector<std::string> words = split(str, ":::");
//последние 2 элемента это поле=значение
int numOfObject = words.size();

//
std::string z = words [0];
if( OBJECTS.end() != OBJECTS.find(z))
{
//ключ присутствует
}
else
{
//создаем
OBJECTS[z] =cJSON_CreateObject();
//клеим к root
cJSON_AddItemToObject(top,z.c_str(), OBJECTS[z]);
}

for (int i=1; i < numOfObject -2; i++)
{
std::string oldz = z;
z = z + ":::" + words [i];

if( OBJECTS.end() != OBJECTS.find(z))
{
//ключ присутствует
}
else
{
//создаем
OBJECTS[z] =cJSON_CreateObject();
//клеим к
cJSON_AddItemToObject(OBJECTS[oldz], words [i].c_str(), OBJECTS[z]);
}
}

std::string value = words [numOfObject-1];
if (type=="string")
{
cJSON_AddStringToObject(OBJECTS[z], words [numOfObject-2].c_str(), value.c_str());
}
else if (type=="number")
{
cJSON_AddNumberToObject(OBJECTS[z], words [numOfObject-2].c_str(), std::stod(value));
}
else if (type=="bool")
{
bool val = false;
if ((value=="true")||(value=="TRUE")) val = true;
cJSON_AddBoolToObject(OBJECTS[z], words [numOfObject-2].c_str(), val);
}
}

void InterfaceForTinCan::PostToLRS(std::string host, std::string login, std::string password)
{
char* out=cJSON_Print(top);
std::string zzz = out;
cJSON_Delete(top);
OBJECTS.clear();

printf("%s\n",out);
free(out);
top=cJSON_CreateObject();

LRS_host = host;
LRS_login = login;
LRS_password = password;

PostStringToLRS(zzz);
}

void InterfaceForTinCan::PostFileToLRS(std::string filename)
{
std::string zzz;
std::string line;
std::ifstream myfile (filename.c_str());
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line);
zzz = zzz + line;
}
myfile.close();
}
//
PostStringToLRS(zzz);
}

void InterfaceForTinCan::PostToFile(std::string filename)
{
char* out=cJSON_Print(top);
std::string zzz = out;
cJSON_Delete(top);
OBJECTS.clear();

std::ofstream myfile;
myfile.open (filename);
myfile << zzz;
myfile.close();

free(out);
top=cJSON_CreateObject();
}

void InterfaceForTinCan::PostStringToLRS(std::string zzz)
{
std::string URL = LRS_host; //"https://cloud.scorm.com/ScormEngineInterface/TCAPI/public/statements";
std::string loginpassword = LRS_login + ":" + LRS_password; //"test:test"

CURL *curl;
struct curl_slist *headers=NULL;

headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append( headers, "Content-Type: application/json");
headers = curl_slist_append( headers, "X-Experience-API-Version:1.0.0");
headers = curl_slist_append( headers, "charsets: utf-8");

curl = curl_easy_init();

if (curl)
{
/* enable verbose for easier tracing */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); //PUT
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
//
curl_easy_setopt( curl, CURLOPT_USERPWD, loginpassword.c_str() ); //"test:test"
// With the curl command line tool, you disable this with -k/--insecure.
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);

curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, zzz.c_str());

std::cout<< "..." << std::endl;
CURLcode res = curl_easy_perform(curl);
std::cout<< std::endl << "..." << std::endl;

/* Check for errors */
if(res != CURLE_OK)
{
std::cout<< "error:" << std::endl;
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
std::cout << std::endl;
}

curl_easy_cleanup(curl);
}
else
{
std::cout << "false" << std::endl;
}
}

std::vector<std::string> InterfaceForTinCan::split(const std::string& s, const std::string& delim, const bool keep_empty)
{
std::vector <std::string> result;
if (delim.empty())
{
result.push_back(s);
return result;
}
std::string::const_iterator substart = s.begin(), subend;
while (true)
{
subend = search(substart, s.end(), delim.begin(), delim.end());
std::string temp(substart, subend);
if (keep_empty || !temp.empty()) {
result.push_back(temp);
}
if (subend == s.end()) {
break;
}
substart = subend + delim.size();
}
return result;
}



Источник: habr.com
К списку статей
Опубликовано: 28.06.2020 18:15:43
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Образование за рубежом

Учебный процесс в it

Cmi5

Lms

Lrs

Xapi

Tincan

Scorm

Имитационные тренажеры

Категории

Последние комментарии

© 2006-2020, personeltest.ru