项目技术要点:
通信协议:http ModbusTCP
工具:Modbus Slave:模拟的是实际的控制设备,相当于服务器端,用于相应主机的请求
Modbus Poll :模拟的是主机,相当于客户端
项目框架:
Web Server
Web Server中文名称叫网页服务器或web服务器。WEB服务器也称为WWW(WORLD WIDE WEB)服务器,主要功能是提供网上信息浏览服务。 Web服务器可以解析HTTP协议。当Web服务器接收到一个HTTP请求,会返回一个HTTP响应,例如送回一个HTML页面。为了处理一个请求,Web服务器可以响应一个静态页面或图片,进行页面跳转,或者把动态响应的产生委托给一些其它的程序例如CGI脚本、JSP脚本、servlets、ASP脚本、服务器端JavaScript或者一些其它的服务器端技术。无论它们的目的如何,这些服务器端的程序通常产生一个HTML的响应来让浏览器可以浏览
LigHttpd
LigHttpd是一个开源的轻量级嵌入式Web server,是提供一个专门针对高性能网站,安全、快速、兼容性好并且灵活的web server环境。具有非常低的内存开销,cpu占用率低,效能好,以及丰富的模块等特点。
CGI简介
CGI(Common Gateway Interface)通用网关接口,是外部扩展应用程序与 Web 服务器交互的一个标准接口。
CGI是Web服务器和一个独立的进程之间的协议,它通过环境变量及标准输入/输出和服务器之间进行数据交互。
1.通过环境变量可以获得网页的请求方式、地址等
2.通过标准输入可以获得网页的消息正文
3.通过标准输出可以发送网页请求的数据
通信方式示意图:
1.当浏览器向web服务器发送动态数据请求时,Web服务器主进程就会Fork创建出一个新的进程来启动CGI程序,也就是将动态脚本交给CGI程序来处理。
2.当CGI程序启动后会去解析动态脚本,然后将结果返回给Web服务器,最后由Web服务器将结果返回给客户端,之前Fork出来的进程也随之关闭。
3.这样,每次用户请求动态脚本,Web服务器都要重新Fork创建一个新进程去启动CGI程序,由CGI程序来处理动态脚本,处理完成后进程随之关闭。
4.对于一个CGI程序,主要的工作是从环境变量和标准输入中读取数据,然后处理数据,最后向标准输出中输出数据。(这里服务器将标准输入和标准输出做了重定向)
CGI工作原理图:
主要代码:
1、modbus tcp 通信主程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "modbus.h"
modbus_t *ctx;
//采集数据
void *handler_data(void *arg)
{
uint16_t dest[64] = {0};
int i;
while (1)
{
modbus_read_registers(ctx, 0, 4, dest);
for (i = 0; i < 4; i++)
printf("data:%d ", dest[i]);
printf("\n");
sleep(5);
}
}
//控制设备
void *handler_ctl(void *arg)
{
int dev, op;
while (1)
{
scanf("%d %d", &dev, &op); //dev: led(0) 蜂鸣器(1) op: 开(1) 关(0)
modbus_write_bit(ctx,dev,op);
sleep(1);
}
}
int main(int argc, char const *argv[])
{
//1.创建modbus实例并初始化
int n = 0;
pthread_t tid1, tid2; //线程id
ctx = modbus_new_tcp(argv[1], atoi(argv[2])); //创建实例
if (ctx == NULL)
{
perror("modbus new tcp error");
return -1;
}
//2.设置从机ID
modbus_set_slave(ctx, 1);
//3.建立连接
if (modbus_connect(ctx) < 0)
{
perror("modbus connect data error");
modbus_free(ctx); //链接失败,释放实例
return -1;
}
if (pthread_create(&tid1, NULL, handler_data, NULL) != 0) //创建信息采集线程
{
perror("pthread_create data err");
return -1;
}
if (pthread_create(&tid2, NULL, handler_ctl, NULL) != 0) //创建控制线程
{
perror("pthread_create ctl err");
return -1;
}
pthread_join(tid1, NULL); //阻塞等待线程结束
pthread_join(tid2, NULL);
//5.关闭套接字
modbus_close(ctx);
//6.释放modbus实例
modbus_free(ctx);
return 0;
}
2.网页端数据显示和设备控制主程序
注:程序中共享内存用于存储获取到的设备端的数据并将数据上报到网页端;消息队列中的数据用于对设备进行控制;
#include <stdio.h>
#include <modbus.h>
#include <modbus-version.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
modbus_t *ctx;
void *read_keep(void *arg)
{
uint16_t dest[16];
while (1)
{
int shmid;
//创建key值
key_t key;
key = ftok("/home/hq/app", 'b');
if (key < 0)
{
perror("ftok err");
return NULL;
}
printf("%#x\n", key);
//创建共享内存
shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
if (shmid < 0)
{
if (errno == EEXIST)
shmid = shmget(key, 128, 0666); //直接打开共享内存
else
{
perror("shmget err");
return NULL;
}
}
printf("shmid:%d\n", shmid);
char *p = NULL;
//映射共享内存
p = (char *)shmat(shmid, NULL, 0);
if (p == (char *)-1)
{
perror("shmat err");
return NULL;
}
modbus_read_registers(ctx, 0, 4, dest);
sprintf(p, "光照强度:%#x 加速度X:%#x 加速度Y:%#x 加速度Z:%#x\n", dest[0], dest[1], dest[2], dest[3]);
printf("光照强度:%#x 加速度X:%#x 加速度Y:%#x 加速度Z:%#x\n", dest[0], dest[1], dest[2], dest[3]);
sleep(5);
}
pthread_exit(NULL);
}
void *write_coil(void *arg)
{
//创建消息队列
//创建key值
key_t key;
key = ftok("/home/hq/app", 'b');
if (key < 0)
{
perror("ftok err");
return NULL;
}
printf("%#x\n", key);
int msgid;
msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if (msgid < 0)
{
if (errno == EEXIST)
{
msgid = msgget(key, 0666);
}
else
{
perror("msgget err");
return NULL;
}
}
printf("msgid:%d\n", msgid);
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char buf[128]; /* message data */
} msg;
while (1)
{
//读取消息
msgrcv(msgid, &msg, sizeof(msg.buf), 0, 0);
modbus_write_bit(ctx, msg.buf[0], msg.buf[2]);
}
pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
ctx = modbus_new_tcp(argv[1], atoi(argv[2]));
modbus_set_slave(ctx, 1);
modbus_connect(ctx);
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, read_keep, NULL);
pthread_create(&thread2, NULL, write_coil, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
modbus_close(ctx);
modbus_free(ctx);
return 0;
return 0;
}
3.CGI主程序
#include "req_handle.h"
#include "log_console.h"
#include <stdio.h>
#include <modbus.h>
#include <modbus-version.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define KB 1024
#define HTML_SIZE (64 * KB)
//普通的文本回复需要增加html头部
#define HTML_HEAD "Content-Type: text/html\r\n" \
"Connection: close\r\n"
/**
* @brief 处理自定义请求,在这里添加进程通信
* @param input
* @return
*/
int parse_and_process(char *input)
{
if (strcmp(input, "get") == 0)
{
int shmid;
//创建key值
key_t key;
key = ftok("./app", 'b');
if (key < 0)
{
perror("ftok err");
return -1;
}
printf("%#x\n", key);
//创建共享内存
shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);
if (shmid < 0)
{
if (errno == EEXIST)
shmid = shmget(key, 128, 0666); //直接打开共享内存
else
{
perror("shmget err");
return -1;
}
}
printf("shmid:%d\n", shmid);
char *p = NULL;
//映射共享内存
p = (char *)shmat(shmid, NULL, 0);
if (p == (char *)-1)
{
perror("shmat err");
return -1;
}
char val_buf[2048] = {0};
strcpy(val_buf, p); //读取共享内存的数据
//将数据写到标准输出(val_buf)
}
else
{
//创建消息队列
//创建key值
key_t key;
key = ftok("./app", 'b');
if (key < 0)
{
perror("ftok err");
return -1;
}
printf("%#x\n", key);
int msgid;
msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if (msgid < 0)
{
if (errno == EEXIST)
{
msgid = msgget(key, 0666);
}
else
{
perror("msgget err");
return -1;
}
}
printf("msgid:%d\n", msgid);
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char buf[128]; /* message data */
} msg;
msg.mtype = 10;
msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0);
}
char val_buf[2048] = {0};
strcpy(val_buf, input);
//数据处理完成后,需要给服务器回复,回复内容按照http协议格式
char reply_buf[HTML_SIZE] = {0};
sprintf(reply_buf, "%sContent-Length: %ld\r\n\r\n", HTML_HEAD, strlen(val_buf));
strcat(reply_buf, val_buf);
log_console("post json_str = %s", reply_buf);
//向标准输出写内容(标准输出服务器已做重定向)
fputs(reply_buf, stdout);
return 0;
}