GoAhead4.1.0 开发总结二(自定义使用)
环境
官方文档:https://www.embedthis.com/goahead/doc/
源码下载: goahead-4.1.0-src.tgz
系统平台:Ubuntu 12.04.4
gcc version 4.6.3
GoAhead自定义程序实现
goahead 源码提供了 test 示例代码,参照 test 代码框架实现 goahead 常用的 GoActions过程和embedded JavaScript 等
1.自定义代码编译与组成
本文实现的代码运行与 arm 平台,为了便于后面源码的编译,单独配置一个makefile配置文件 .mk 文件用于自定义程序的编译
复制 projects/goahead-linux-default.mk 重命名为 goahead-linux-arm-default.mk,并在此基础上做修改
后面每次编译采用指定文件的方式编译
make -f projects/goahead-linux-arm-default.mk
在 goahead-4.1.0 下新建 m283-webs 文件夹,里面存放自定义 web 代码,包含文件如下所示
goahead-linux-arm-default.mk 文件修改如下:
修改1:平台与配置
主要修改 ARCH(平台) CC(编译环境) 以及编译的常用配置,可以用于裁剪代码大小,本例先放弃SSL,因为这个比较占ROM
# # goahead-linux-default.mk -- Makefile to build Embedthis GoAhead for linux # NAME := goahead VERSION := 4.1.0 PROFILE ?= default #ARCH ?= $(shell uname -m | sed 's/i.86/x86/;s/x86_64/x64/;s/arm.*/arm/;s/mips.*/mips/') ARCH := arm CC_ARCH ?= $(shell echo $(ARCH) | sed 's/x86/i686/;s/x64/x86_64/') OS ?= linux #CC ?= gcc CC := arm-fsl-linux-gnueabi-gcc CONFIG ?= $(OS)-$(ARCH)-$(PROFILE) BUILD ?= build/$(CONFIG) LBIN ?= $(BUILD)/bin PATH := $(LBIN):$(PATH) ME_COM_COMPILER ?= 1 ME_COM_LIB ?= 1 ME_COM_MATRIXSSL ?= 0 ME_COM_MBEDTLS ?= 1 ME_COM_NANOSSL ?= 0 ME_COM_OPENSSL ?= 0 ME_COM_OSDEP ?= 1 ME_COM_SSL ?= 0 ME_COM_VXWORKS ?= 0 ME_COM_OPENSSL_PATH ?= "/usr/local/openssl"
修改2:自定义代码的编译
在 goahead-linux-arm-default.mk 文件中添加对自定义程序 m283-webs 的编译支持
# # m283-webs.o # 47 DEPS_47 += $(BUILD)/inc/goahead.h DEPS_47 += $(BUILD)/inc/js.h $(BUILD)/obj/m283-webs.o: \ m283-webs/m283-webs.c $(DEPS_47) @echo ' [Compile] $(BUILD)/obj/m283-webs.o' $(CC) -c -o $(BUILD)/obj/m283-webs.o $(CFLAGS) $(DFLAGS) -D_FILE_OFFSET_BITS=64 -D_FILE_OFFSET_BITS=64 -DMBEDTLS_USER_CONFIG_FILE=\"embedtls.h\" -DME_COM_OPENSSL_PATH=$(ME_COM_OPENSSL_PATH) $(IFLAGS) "-I$(ME_COM_OPENSSL_PATH)/include" m283-webs/m283-webs.c # # m283-webs # 48 DEPS_48 += $(BUILD)/bin/libgo.so DEPS_48 += $(BUILD)/.install-certs-modified DEPS_48 += $(BUILD)/obj/m283-webs.o ifeq ($(ME_COM_MBEDTLS),1) LIBS_48 += -lmbedtls endif ifeq ($(ME_COM_MBEDTLS),1) LIBS_48 += -lgoahead-mbedtls endif ifeq ($(ME_COM_MBEDTLS),1) LIBS_48 += -lmbedtls endif ifeq ($(ME_COM_OPENSSL),1) LIBS_48 += -lgoahead-openssl endif ifeq ($(ME_COM_OPENSSL),1) ifeq ($(ME_COM_SSL),1) LIBS_48 += -lssl LIBPATHS_48 += -L"$(ME_COM_OPENSSL_PATH)" endif endif ifeq ($(ME_COM_OPENSSL),1) LIBS_48 += -lcrypto LIBPATHS_48 += -L"$(ME_COM_OPENSSL_PATH)" endif LIBS_48 += -lgo ifeq ($(ME_COM_OPENSSL),1) LIBS_48 += -lgoahead-openssl endif ifeq ($(ME_COM_MBEDTLS),1) LIBS_48 += -lgoahead-mbedtls endif $(BUILD)/bin/m283-webs: $(DEPS_48) @echo ' [Link] $(BUILD)/bin/m283-webs' $(CC) -o $(BUILD)/bin/m283-webs $(LDFLAGS) $(LIBPATHS) "$(BUILD)/obj/m283-webs.o" $(LIBPATHS_48) $(LIBS_48) $(LIBS_48) $(LIBS) $(LIBS)
2.编译与测试
复制 test.c 代码框架 到 m283-webs.c 来测试,后面按需修改
make -f projects/goahead-linux-arm-default.mk
在 build/linux-arm-default/bin 下生成了我们自定义程序的可执行文件 m283-webs ,将其拷贝到 nfs 的web调试目录
通过上节可知,web 运行需要包含的文件如下所示
在 web 中放入简单的 html 文件
在目标板测试 运行正常
GoAhead 常用操作实现
GoActions 过程(旧版本使用的是GoForms,使用方式基本相同),绑定C函数为具体的URL链接,主要用于用于响应用户输入,更新设置或执行特定动作。
一般过程:
1.通过 websDefineAction 函数注册GoAction 函数,即绑定该 C 函数到 /action/test
2.在 route.txt 中添加对应action路径
route uri=/action handler=action
3.在html 文件中的表单中触发 http POST操作指定URL /action/test
这样当html 表单触发post时就会调用注册好的 action函数 test
在新版本中,GoActions 过程的函数定义均可放在自定义程序中,下面是 m283-webs.c goaction过程的实现部分代码
m283-webs.c
MAIN(goahead, int argc, char **argv, char **envp) { char *argp, *home, *documents, *endpoints, *endpoint, *route, *auth, *tok, *lspec; int argind, duration; #if WINDOWS if (windowsInit() < 0) { return 0; } #endif route = "route.txt"; auth = "auth.txt"; duration = 0; // 命令行解析 for (argind = 1; argind < argc; argind++) { argp = argv[argind]; if (*argp != '-') { break; } else if (smatch(argp, "--auth") || smatch(argp, "-a")) { if (argind >= argc) usage(); auth = argv[++argind]; #if ME_UNIX_LIKE && !MACOSX } else if (smatch(argp, "--background") || smatch(argp, "-b")) { websSetBackground(1); #endif } else if (smatch(argp, "--debugger") || smatch(argp, "-d") || smatch(argp, "-D")) { websSetDebug(1); } else if (smatch(argp, "--duration")) { if (argind >= argc) usage(); duration = atoi(argv[++argind]); } else if (smatch(argp, "--home")) { if (argind >= argc) usage(); home = argv[++argind]; if (chdir(home) < 0) { error("Cannot change directory to %s", home); exit(-1); } } else if (smatch(argp, "--log") || smatch(argp, "-l")) { if (argind >= argc) usage(); logSetPath(argv[++argind]); } else if (smatch(argp, "--verbose") || smatch(argp, "-v")) { logSetPath("stdout:2"); } else if (smatch(argp, "--route") || smatch(argp, "-r")) { route = argv[++argind]; } else if (smatch(argp, "--version") || smatch(argp, "-V")) { printf("%s\n", ME_VERSION); exit(0); } else if (*argp == '-' && isdigit((uchar) argp[1])) { lspec = sfmt("stdout:%s", &argp[1]); logSetPath(lspec); wfree(lspec); } else { usage(); } } documents = ME_GOAHEAD_DOCUMENTS; if (argc > argind) { documents = argv[argind++]; } initPlatform(); // 平台初始化,注册信号处理函数 if (websOpen(documents, route) < 0) // 初始化服务器 { error("Cannot initialize server. Exiting."); return -1; } #if ME_GOAHEAD_AUTH // 加载路径和鉴权配置文件 if (websLoad(auth) < 0) { error("Cannot load %s", auth); return -1; } #endif logHeader(); // 打印web服务器基本信息 if (argind < argc) { while (argind < argc) { endpoint = argv[argind++]; // 参数中指定了服务器的endpoint 如./goahead -v ./web/ 192.168.10.111:80 // WEB端口监听 if (websListen(endpoint) < 0) { return -1; } } } else { endpoints = sclone(ME_GOAHEAD_LISTEN); for (endpoint = stok(endpoints, ", \t", &tok); endpoint; endpoint = stok(NULL, ", \t,", &tok)) { #if !ME_COM_SSL if (strstr(endpoint, "https")) continue; #endif if (websListen(endpoint) < 0) { wfree(endpoints); return -1; } } wfree(endpoints); } #if ME_ROM && KEEP // 采用web文件ROM化 /* If not using a route/auth config files, then manually create the routes like this: If custom matching is required, use websSetRouteMatch. If authentication is required, use websSetRouteAuth. */ websAddRoute("/", "file", 0); #endif #ifdef GOAHEAD_INIT /* Define your init function in main.me goahead.init, or configure with DFLAGS=GOAHEAD_INIT=myInitFunction */ { extern int GOAHEAD_INIT(); if (GOAHEAD_INIT() < 0) { exit(1); } } #endif websDefineHandler("test", testHandler, 0, 0, 0); // 定义一个请求处理程序 websAddRoute("/test", "test", 0); #if ME_GOAHEAD_LEGACY // 表示对 /goform 的请求都交给 websFormHandler 函数处理。函数的参数列表如下 websUrlHandlerDefine("/legacy/", 0, 0, legacyTest, 0); // 设置form方式调用时候的文件位置 // websFormDefine(T("odbc_form_web_login"), odbc_form_web_login); // 定义form方式调用接口函数字符对应的函数名称 #endif #if ME_GOAHEAD_JAVASCRIPT websDefineJst("aspTest", aspTest); // 定义一个js本地函数 websDefineJst("bigTest", bigTest); #endif websDefineAction("test", actionTest); // goAction定义,在asp文件中调用C函数 websDefineAction("sessionTest", sessionTest); websDefineAction("showTest", showTest); websDefineAction("led", on_led_set); websDefineAction("buzzer", on_buzzer_set); #if ME_GOAHEAD_UPLOAD && !ME_ROM websDefineAction("uploadTest", uploadTest); #endif #if ME_UNIX_LIKE && !MACOSX /* Service events till terminated */ if (websGetBackground()) { if (daemon(0, 0) < 0) { error("Cannot run as daemon"); return -1; } } #endif if (duration) { printf("Running for %d secs\n", duration); websStartEvent(duration * 1000, (WebsEventProc) exitProc, 0); } websServiceEvents(&finished); logmsg(1, "Instructed to exit\n"); websClose(); #if WINDOWS windowsClose(); #endif return 0; } static void actionTest(Webs *wp) { cchar *name, *address; name = websGetVar(wp, "name", NULL); address = websGetVar(wp, "address", NULL); websSetStatus(wp, 200); websWriteHeaders(wp, -1, 0); websWriteEndHeaders(wp); websWrite(wp, "<html><body><h2>name: %s, address: %s</h2></body></html>\n", name, address); websFlush(wp, 0); websDone(wp); } // led控制 static void on_led_set(Webs *wp) { // get the input value in query stream char *io_val; io_val = websGetVar(wp, "val_led", NULL); if(io_val) system("echo 1 > /sys/class/leds/led-err/brightness"); else system("echo 0 > /sys/class/leds/led-err/brightness"); } // 蜂鸣器控制 static void on_buzzer_set(Webs *wp) { char *io_val; io_val = websGetVar(wp, "val_buz", NULL); if(io_val) system("echo 1 > /sys/class/leds/beep/brightness"); else system("echo 0 > /sys/class/leds/beep/brightness"); }
默认首页 index.html 添加 test 页面链接
<html><head><title>index.html</title></head> <body>Hello /index.html</body> <p>Link to reload <a href="index.html">this page</a></p> <p>Link to <a href="/action/logout">log out</a></p> <p>Link to <a href="test.html">test.html</a></p> </html>
测试的网页 test.html
<!DOCTYPE html> <html> <head> <title>test.html</title> <meta charset="UTF-8"> </head> <body> <p>Please log in</p> <form action=/action/test method="post"> <table> <tr><td>账号:</td><td><input type="text" name="name"></td></tr> <tr><td>密码:</td><td><input type="password" name="address"></td></tr> <tr><td><input type="submit" name="submit" value="submit"></td> <td><input type="reset" value="reset"></td></tr> </table> </form> <br /> <p align="left"><b> 蜂鸣器控制示例</p> <form id="gpio-buzzer" action="/action/buzzer" method="post"> <input type="hidden" name="lab_buzzer" value="buzzer" /> <table> <tr> <td width=100> BUZZER</td> <td width=100> <input type="checkbox" name="dir_buz" checked disabled title="out" />out</td> <td width=100> <input type="checkbox" name="val_buz" /></td> <td width=100><input type="submit" value="set" /></td> </tr> </table> </form> <br /> <p align="left"><b> LED控制示例</p> <form id="gpio-led" action="/action/led" method="post"> <input type="hidden" name="lab_led" value="led" /> <table> <tr> <td width=100> BUZZER</td> <td width=100> <input type="checkbox" name="dir_led" checked disabled title="out" />out</td> <td width=100> <input type="checkbox" name="val_led" /></td> <td width=100><input type="submit" value="set" /></td> </tr> </table> </form> <script type="text/javascript"> function loadXMLDoc() { var xmlhttp; if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp=new XMLHttpRequest(); } else {// code for IE6, IE5 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("myDiv").innerHTML=xmlhttp.responseText; } } xmlhttp.open("GET","action/test",true); xmlhttp.send(); } </script> <div id="myDiv"><h2>需要刷新的局部内容</h2></div> <button type="button" onclick="loadXMLDoc()">通过 AJAX 实现局部刷新</button> <p align="left"><b> © 2019 HTGD Co.,Ltd.</b> <a href="http://www.htgd.com.cn">公司主页</a></p> </body> </html>
每次重新编译之后,拷贝 m283-webs 可执行文件到测试nfs文件夹中
目标机 挂载nfs
运行 ./m283-webs -v /web/
在浏览器输入 192.168.1.124 点击test.html链接
或直接输入 192.168.1.124/test.html
显示效果
测试 goaction 交互
填写内容 点击 submit,web 返回正常
指示灯、蜂鸣器等web操作目标板硬件,测试正常;
至此完成了 web 的部分内容交互,但该交互的页面刷新时整个页面的刷新,非常不方便;
下一节实现部分页面刷新数据交互 Embedded Javascript,在网页上为 Ajax 技术。