`
裴小星
  • 浏览: 267309 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
8ccf5db2-0d60-335f-a337-3c30d2feabdb
Java NIO翻译
浏览量:27919
F3e939f0-dc16-3d6e-8c0b-3315c810fb91
PureJS开发过程详解
浏览量:75080
07a6d496-dc19-3c71-92cf-92edb5203cef
MongoDB Java ...
浏览量:63427
社区版块
存档分类
最新评论

OMToolkit: Web Server,Web Framework 及 Object-Orinted Database 的简单实现

阅读更多

概述

 

  最近在看一些NIO和concurrent的资料,写了一个小项目作为练习,看起来涉及面很广,但上实现方式很简单,整个项目共2179行。
  项目已经上传到Google Code,地址:http://code.google.com/p/oh-my-toolkit/。可以在Downloads页面下载已经发布的0.0.1版本的OMToolikit以及OMSimpleBlog(一个示例,仅254行),也可以通过SVN下载源码。

  OMToolkit除JDK 1.6之外不依赖于任何第三方包,本身包含了Web Server,Web Framework 和 Object-Orinted Database 三个部分, 以“Entity”概念作为提供给Web应用开发者的API。
  最近将连续发布一系列文章,重点说明实现思路,同时也总结学习所得。


使用示例

  首先以OMSimpleBlog为例,简要说明OMToolkit的使用方式。后续的系列文章将对这个例子进行更详细的说明。

 

 

  1.Entity建模
  Entity的属性分为两类,一类继承自OMField,包括StringField,DateField,Children等,这些属性将被持久化。
另一类为基本类型,如String,int等,这些属性实际上是视图属性,不会被持久化,但可以通过URL进行赋值,并在渲染对象时获取。
基于OMToolkit的Web应用必须有一个名为Database的Entity,OMSimpleBlog中,Database的建模方式如下:

public class Database extends Entity {
	private Children<User> userList;
	private References<Article> articleList;

	// Methods ...
}

 

  可以看到,Database包含了一个User列表和一个Article列表。Childern/Child与References/Reference的区别在于:
  1. 删除一个对象时将会级联地删除Child和Children(通过调用Entity.doDelete()),而以References/Reference方式引用的对象不会被删除。
  2. 从Children/Child中添加或移除移除一个对象时,这个对象将被持久化或删除,而References/Reference与持久化无关。

  或者说,References/Reference只是简单地表示引用关系,与对象持久化无关;而Childern/Child表示严格的父子关系,与对象持久化密切相关。

  接着看User的建模:

public class User extends Entity {
	private StringField name;
	private StringField password;
	private StringField blogName;
	private Children<Article> articleList;

	// View Attributes ...

	// Methods ...
}

  可以看到,user包含了name,password,blogName三个字符串型的Field,以及一个Article的列表。并且,User与Article之间是严格的父子关系。
  在这个简单的示例中,由于Blog的属性比较简单,只有blogName,因此直接合并到了User中;并且Blog中没有分类(Category),因此User直接与Article关联。
  如果Blog的属性比较复杂,并且带有分类,则User的建模方式可能是这样的:

public class User extends Entity {
	private StringField name;
	private StringField password;
	private Child<Blog> blog;
	private Children<Category> categoryList;

	// View Attributes ...

	// Methods ...
}

 
  这里需要说明的是*List这种命名方式,如果调用Entity的内置的doDelete(),doAdd()等方法,将会以这种默认的命名方式中父类中寻找相关的集合,然后将自己从这个集合中移除(或增加到集合中)。如果不以这种方式命名,那么就需要自己编写这些方法了。
  关于Child/Reference的延迟加载:当调用Child/Reference的get()方法时,所引用的对象才会真正地被加载,这也是一种性能上的优化。

  最后是Article的建模:

public class Article extends Entity {
	private DateField published;
	private StringField title;
	private TextField summary;
	private TextField content;
	private Reference<User> user;

	// View Attributes ...

	// Methods ...
}

  这里需要特别说明的是TextField类型:TextField类型实际上继承了Child<Text>,Text是特殊的Entity,这是为了利用延迟加载的特性;在参数解析和视图解析中对TextField进行了特殊处理,以保证使用上与StringField相似,同时还可以在视图中使用类似"${content.html}"的方法,将内容转为Html进行渲染(不过这个方法还有待完善)。

 

  2.Rest风格URL与参数传递

 

  举个例子:URL:http://localhost/User/save/name/aaa/password/bbb/blogName/ccc 将会创建一个User对象,将name赋值为aaa,将password赋值为bbb,将blogName赋值为ccc,并调用该对象的save()方法。当然,这种通过URL进行赋值的方式也可能带来安全隐患,尤其是当需要对对象进行更新并持久化时。因此,OMToolkit引入了更新选项(UpdateOptions)对这种赋值进行限制,这将在后面谈到。

  另外,如果URL中包含id,则对象会被自动获取(通常是从cache中clone一个对象);如果URL中包含parent的id,则parent也会被自动获取(否则认定parent为database),这在增删对象和显示特定parent的列表时非常有用。

 

  3.Session,Cookie,Request

 

  可以随意地在Entity中声明session和cookies并使用,方法如下:

public class User extends Entity {
	// Fields ...

	private Session session;
	private List<Cookie> cookies;

	// Other view attributes ...

	// Other methods ...	

	public String checkLogin() throws Exception {
		name.set(Cookie.get(cookies, "name"));
		password.set(Cookie.get(cookies, "password"));

		// Other codes ..
	}

	public String login() throws Exception {
		// Other codes ...

		if (remember) {
			cookies.add(new Cookie("name", name.get()));
			cookies.add(new Cookie("password", password.get()));
		}

		// Other codes ...
	}

	public String logout() throws Exception {
		session.set("userId", null);
		session.set("role", null);
		cookies.add(new Cookie("name", ""));
		cookies.add(new Cookie("password", ""));
		return toView("/Home/login");
	}

	// Annotations ...
	public String save() throws Exception {
		// Other codes ...

		session.set("role", "user");
		session.set("userId", getId());

		// Other codes ..
	}
	
	// Other methods ...
}

 

  事实上,在生成Entity时,OMToolkit将会自动检查Entity的属性,并对其中的session和cookies进行赋值(如果有的话)。

  Request对象可以通过Entity.getRequest()获取,但不推荐,因为实际上通过Fields和View Attributes就可以接收和设置参数了。


  4.权限控制与更新选项

 

  使用注解进行权限控制,并标明更新选项,方法如下:

public class User extends Entity {
	@Role("user")
	@UpdateOptions(toUpdate = "blogName[20]")
	public String update() throws Exception {
		// Codes ...
	}
}

   其中,Role注解可以放置在类或方法上,而UpdateOptions只能放置在方法上。

  Role的value为数组,@Role({"user", "admin"}) 会从session中取出key为"role"的对象(String类型,目前还不支持数组类型),看是否包含于允许列表中,若不包含,则转向登录页面。登录页面可以在配置文件(Cfg.cfg)中进行配置。

  UpdateOptions实际上是对输入URL进行检查,避免不合法的赋值,有toUpdate和allowEmpty两个属性。不合法的URL将被重定向到登陆页面。另外,在带有UpdateOptions的方法中获取对象时会申请互斥的更新锁,这是出于数据一致性的考虑,但实际上有潜在的性能问题,也容易导致死锁,是后续版本中需要优化的一个重点。


  5.事务控制与自动提交

 

  可以使用getTransaction()获取对事务的控制,OMToolkit在正常执行完一次操作时也会自动提交(有异常则rollback),对于没有添加@UpdateOptions注解的方法,将不会进行持久化或删除数据。这时transaction实际上只用于cache本次事务中获取的对象。

  示例如下:

@Role("user")
@UpdateOptions(toUpdate = "blogName[20]")
public String update() throws Exception {
	// Other Codes ...

	if (userId == null || (Long) userId != getId()) {
		getTransaction().rollback();
		return toView("/Home/login");
	}

	// Other Codes ...
}

 
  6.分页与排序


  Children/References的toList()方法将会按照id降序排列(即新创建的对象会排在前面),PageUtil.run(References<T>,Request)方法提供分页功能,写法如下:

pagedArticles = PageUtil.run(articleList, getRequest());

  该方法将自动按照URL中的pageIndex和pageSize进行分页和排序,排序的依据仍然是id降序。
  如果需要自定义的排序,可以通过Children/References的values()方法获取Child/Reference的集合,并将一个自定义的Comparator、pageIndex、pageSize一同传入ArrayUtil的toList(...)方法中。当然这种方式比较繁琐,在后续版本(varsion 0.0.3)中计划加入更便捷的排序和过滤API,实现类似下面的写法:

 

articleList.where("title like 'OMTookit%'")
	.orderBy("published desc")
	.toList(pageIndex, pageSize);  

 

  7.CRUD

 

  下面快速地浏览一下OMTookit中的CRUD(User类中的方法):

 

  列出用户

@Role("admin")
public String list() throws Exception {
	return super.list();
}

 

  这里直接使用了Entity的list()方法,Entity.list()实现如下:

public String list() throws Exception {
	return toView(PageUtil.run(doList(), request));
}

 

  获取用户(同时显示该用户的blog中的所有文章):

public String get() throws Exception {
	pagedArticles = PageUtil.run(articleList, getRequest());
	return toView();
}

   请求URL中包含了id,该对象将自动被获取,所以不需要编写获取对象的语句。


  注册新用户

@UpdateOptions(toUpdate = { "name[20]", "password[20]", "blogName[20]" })
public String save() throws Exception {
	if (exists()) {
		error = cfg("exists") + '\n';
		return toView("/Home/register");
	}

	doSave();
	session.set("role", "user");
	session.set("userId", getId());

	return toView("/Home/index");
}

  首先检查用户是否存在,若存在则设置错误信息并回到注册页面;doSave()方法会先在parent中找到"userList",再将自身添加到到列表中。之后在提交事务时将自动保存被创建的用户,并更新parent(即Database)。之后是session的设置,表示注册后自动登录。
  除去检查用户是否存在和session设置的代码,实际实现保存用户的只有doSave()这一句。


  更新用户(只更新博客名称):

@Role("user")
@UpdateOptions(toUpdate = "blogName[20]")
public String update() throws Exception {
	Object userId = session.get("userId");
	if (userId == null || (Long) userId != getId()) {
		getTransaction().rollback();
		return toView("/Home/login");
	}

	return action("edit");
}

  先检查用户的id是否与被修改的用户的id相同(防止非法修改他人的博客名称),然后返回到编辑页面。
  看起来好像没有更新语句,这是因为OMTookit将自动保存被更改过的Entity(对于带有@updateOptions注解的方法)。需要注意的是getTransaction().rollback()是必不可少的,否则对象仍然会被更新(因为对象被更改过了)。这是少数需要开发者手动控制事务的地方。看起来好像这种自动保存机制带来了麻烦,但在大多数情况下应该还是会方便开发者的。

 

  删除用户

 

	@Role("admin")
	@UpdateOptions
	public String delete() throws Exception {
		return super.delete();
	}

   删除一个Entity的同时会删除它的child和children。

 

  Entity本身有默认的CRUD实现,当然,大多数情况下还是需要开发者覆盖这些实现。

  需要注意的有三点:

  1. 如果在URL中指定id,则相应的对象将被自动获取
  2. 如果在URL中指定parent的id,则相应的parrent将被自动加载,否则认定parent为Database。
  3. 对于带有UpdateOptions注解的方法,OMTookit将自动保存被更新过的对象。

 

  如果所请求的方法不存在,将自动调用toView()方法。所以Article没有实现get()方法(Entity中也没有),因为实际上不需要。只要在URL中指定了id,对象就会被获取,然后调用toView()方法,渲染该Entity。


  8.视图渲染

 

  继承与包含

  以views/Home/index.html为例:

#extends /Master/home

#override header
<a href="/User/get/id/${userId}/pageSize/10">我的博客</a>
<a href="/User/edit/id/${userId}/pageSize/10">博客设置</a>
<a href="/User/logout">注销登录</a>

#override content
#include /Article/list/action/index/pageIndex/${pageIndex}/pageSize/10

   被继承的views/Master/home.html如下:

#extends /Master/master

#override title
博客首页

#override content
<div id="header">
	#abstract header
</div>
<h1>Oh, My Simple Blog!</h1>
#abstract content

  可以看到,#extends表示继承,views/Home/index.html继承了views/Master/home.html,而views/Master/home.html又继承了views/Master/master.html。#override表示覆盖,将会替换被继承视图的#abstract部分。
  #include表示包含,后面接上被包含的URL。
  需要注意的是,继承是静态的,而包含是动态的;即继承只是简单的替换,而包含则会在内部重新模拟一次请求。


  循环与分支

#loop var
...(block) 
#end

  有两种形式:不带参数和带一个参数,不带参数指对当前Model进行遍历,带参数则表示先获取该属性,再进行遍历。

#if var
...(block) 
#end 

  带一个参数,该参数的toString()方法返回"true"则解析语法块中的内容,否则不解析。

 

  变量解析

  解析的顺序是Model的get方法 -> Model属性 -> request -> session。

  如"${pageIndex}",会先查找当前Model的getPageIndex()方法,然后是pageIndex属性,接着是request.params().get("pageIndex"),最后是session.get("pageIndex") 。都为null则作为空字符串进行渲染。


  点操作符
  "." 解析顺序是 Model的get方法 -> Model属性
  如"content.html" 会先找content对象的getHtml()方法,然后找名为“html”的属性。


  9.配置文件


  有两类配置文件,一类是Cfg.cfg,实际上主要配置的是Web Server的参数,包括端口、缓冲区大小、线程池的线程数等。

  另一类是cfg文件夹下的文件,以Entity类的名称命名,如User.cfg,主要用于配置一些提示信息等;可以用Entity.cfg(String name, String... args)获取配置文件中的值。


  10.打包发布


  将Web应用打包为jar(eclipse导出时只需勾选src文件夹),放在空模板中(即OMToolkit_0_0_1_bin.rar,可以在Google Code项目主页下载), 注意根据打包文件的名称,修改Cfg.cfg中的jar属性,并将cfg、data、resources、views目录复制到空模板中,覆盖原来的文件夹。(0.0.3 版本中会增加Ant脚本进行打包)


实现思路

 

  1.Web Server的实现

  Server的实现主要位于包com.omc.server。主要借助了Java NIO和concurrent包中的线程池。
  网络信息的读写是单线程的,即每次从每个连接中读写一部分数据,而请求的处理则借助线程池。每次读写的数据量和线程池的线程数可以在Cfg.cfg中进行配置。
  Server类的核心代码:

public class Server {
	public void run() throws Exception {
		init();
		Selector selector = openSelector();
		while (true) {
			doSelect(selector);
		}
	}

	private Selector openSelector() throws Exception {
		Selector selector = Selector.open();
		ServerSocketChannel server = ServerSocketChannel.open();

		server.configureBlocking(false);
		SelectionKey key = server.register(selector, SelectionKey.OP_ACCEPT);
		key.attach(new Accepter(key));

		server.socket().bind(new InetSocketAddress(Cfg.port()));

		return selector;
	}

	private void doSelect(Selector selector) throws Exception {
		selector.select();
		Set<SelectionKey> selected = selector.selectedKeys();

		Iterator<SelectionKey> it = selected.iterator();
		while (it.hasNext()) {
			((OMRunnable) it.next().attachment()).run();
		}

		selected.clear();
	}
	
	// Other methods ...
}

 

  在openSelector()方法中,将一个Accepter类的对象附加到了ServerSocketChannel的key上,当key的状态为acceptable时,将被selector检索到,从而调用Accept对象的run()方法,以处理接收到的SocketChannel,Accepter的核心代码如下:

public class Accepter implements OMRunnable {
	// Other codes ...

	public void run() throws Exception {
		SocketChannel socket = accept();
		socket.configureBlocking(false);

		SelectionKey k = register(socket);
		k.attach(new Worker(k));
	}

	private SocketChannel accept() throws Exception {
		return ((ServerSocketChannel) key.channel()).accept();
	}

	private SelectionKey register(SocketChannel socket) throws Exception {
		return socket.register(key.selector(), SelectionKey.OP_READ);
	}
}

 

  将被接收到的SocketChannel也注册到selector上,这样一来,当SocketChannel的key的状态为readable时,也会被selector检索到,从而调用Worker的run()方法。
  Woker的实现较复杂,包含了Reading、Processing、Writing三个内部类,分别表示读取、处理、写回三种状态。Reading类每次读取一部分数据,读取完毕后创建一个Processing对象,并提交到线程池中(同时key的interest被设置为0,表示不关心任何操作);线程池在有线程空闲的情况下启动Processing对象的处理过程,处理完成后,将状态改为Writing(同时key的interest被设置为SelectionKey.OP_WRITE,表示只关心写操作)。


  2.Web Framework的实现
  Web Framework的部分主要位于com.omc.web包,实现了分发任务的控制器(Controller)、事务控制类(Transaction)、视图类(View)等。
  Controller类主要通过从URL中解析得到的信息,创建Entity对象,对该对象的属性进行赋值,并调用相应的方法处理请求,最后进行视图渲染。主要运用了Java的反射机制。

  Controller核心代码如下:

public class Controller {
	// Other codes ...

	public byte[] run() throws Exception {
		if (!AccessChecker.isAllow(req)) {
			return toLoin();
		}

		String res = doRun();
		List<Cookie> cookies = getCookies(entity);
		return response(res, cookies).getBytes();
	}

	public String doRun() throws Exception {
		Method method = req.method();
		UpdateOptions op = method.getAnnotation(UpdateOptions.class);
		Transaction transaction = loadEntity(op);
		try {
			Object res = method.invoke(entity);
			transaction.commit();
			return (String) res;
		} catch (Exception e) {
			transaction.rollback();
			e.printStackTrace();
			return "";
		}
	}

	// Other codes ...
}

  先检查权限、更新选项等,一旦发现非法访问,则重定向至登录页面;如果通过检查,则先加载Entity,然后用反射调用相应的方法,执行过程中没有错误则提交事务,否则回滚事务。

  View的核心代码如下:

public class View {
	// Other codes ...

	public static String render(Object model, String view, Request request)
			throws Exception {
		String content = read("views/" + view + ".html").replace("\r", "");
		return new View(model, content, request).render();
	}

	private View(Object model, String content, Request request) {
		this.model = model;
		this.content = content;
		this.req = request;
	}

	public String render() throws Exception {
		if (content.startsWith("#extends")) {
			content = doExtends(content);
			return new View(model, content, req).render();
		}

		lines = StringUtil.split(content, '\n');
		while (index < lines.size()) {
			readLine();
			if (s.startsWith("#if")) {
				branch();
			} else if (s.startsWith("#loop")) {
				loop();
			} else {
				parseLine();
			}
		}

		return result.toString();
	}

	// Other codes ...
}

  可以看到,View的构造函数是私有的,只能通过调用静态方法View.render(...)来渲染视图。
  首先检查"#extends",如果存在则对内容进行替换,然后重新构造View并调用render()方法。然后逐行解析,遇到"#if"则进行分支处理,遇到"#loop"则进行循环处理,否则进行一般的解析。

 

  3.Object-Orinted Database的实现

 

  OODB的部分主要位于com.omc.data包,主要实现类包括DataUtil和FieldUtil。例如,DataUtil.get(long id, boolean forUpdate)方法用于获取一个对象:

	public static Entity get(long id, boolean forUpdate) throws Exception {
		if (forUpdate) {
			while (locks.contains(id)) {}
			locks.add(id);
		}

		Entity entity = cache.get(id);
		return entity == null ? loadEntity(id) : entity;
	}

 

  loadEntity(long id)方法如下:

	private static Entity loadEntity(long id) throws Exception {
		Meta meta = Meta.get(id);
		Entity entity = meta.getEntity();

		loadFields(entity, meta);

		entity.setId(id);
		cache.add(entity);

		return FieldUtil.clone(entity);
	}

 

     loadFields(Entity entity, Meta meta)方法如下:

	private static void loadFields(Entity entity, Meta meta) throws Exception {
		long position = meta.getPosition();
		int size = meta.getSize();
		String content = FileUtil.read(DATA_FILE, position, size);
		parseFields(entity, content);
	}

 

  下面是对一些要点的说明:

 

  meta与data
  数据文件包括data/meta和data/data两个部分,分别包含了元数据(id,对象数据在data文件中的位置,Entity的类型)和对象数据。
  OMTookit启动时,加载所有的meta(这可能会导致内存占用过高,是一个需要优化的地方)。
  读取对象时,先从meta中读取对象数据所在位置,然后解析对象数据,生成对象。
  保存对象时,将对象按一个的规则序列化并保存到data文件中,同时在meta文件中记录数据。

 

  cache

  对于已经加载的数据,在一定时间(可以在Cfg.cfg中配置)内将存在于cache中,再次读取时只需要clone一份,而不需要从数据文件中读取。

 

  更新锁
  当读取的对象的目的是更新("forUpdate")时,将申请该对象的更新锁,这意味着将阻塞其他"forUpdate"方式的读取。这可能是一个潜在的性能瓶颈,是后续版本需要进行优化的一个地方。

 

 


版本计划

  偶数版本(0.0.2,0.0.4...)均为Bug修复与重构,奇数版本增加新功能。

 

  0.0.3 计划
  1. API改进,力求实现更为便捷的API
  2. 文件上传
  3. 404、500、502等错误页面
  4. 排序、筛选、全文搜索等功能
  5. 日志、性能、异常: 可以使用动态代理。OMField 和 Entity 也可以用动态代理加以改造。
  6. 视图引擎增强。

  7. XML、JSON支持。

  8. Ant打包脚本。

 

  0.0.5计划
  1. 数据存储性能优化(更新锁机制,多级Meta)

 

  更新锁需要改进,避免过多的互斥。
  meta全量加载可能导致性能问题。如果将meta本身也视为数据的话,实际上可以建立meta的meta,进而建立n层的meta,从而实现更小的加载量。

 

  2. 数据导入、导出、迁移工具。


  0.0.7计划
  1. Server性能优化。
  2. 多应用支持。目前一个Server只支持运行一个应用。

 

  每个小版本的周期约为1.5个月,大版本的周期约为1年。


系列文章发布计划

 

  1. OMSimpleBlog详细说明(3月17日)
  从零开始搭建OMSimpleBlog,主要分为用户注册与登录,文章发布与编辑,后台数据管理三个步骤。

 

  2. OMTookit Web Server 详细说明(3月19日)
  从零开始搭建Server,分为Server、Acceptpr、Worker的实现;Session、Cookie的处理。

 

  3. OMToolkit Web Framework 详细说明(3月21日)
  在前一部分的基础上,开发Web Framework,包括Controller的实现,Transaction的实现,以及View的实现。

 

  4. OMToolkit Object-Orinted Database 详细说明(3月24日)
  在前一部分的基础上,开发数据存储部分,包括meta data、对象的持久化、对象的读取、cache、更新锁等。

 

  5. 总结(3月26日)

12
10
分享到:
评论
4 楼 裴小星 2011-05-14  
key232323 写道
douyu2.0??

楼主真够有精力了,不过很支持下!

我下载下看看web server性能如何?

oodb这个,我觉得实用性太低。


现在只是玩具而已,确实不实用。
3 楼 key232323 2011-05-13  
douyu2.0??

楼主真够有精力了,不过很支持下!

我下载下看看web server性能如何?

oodb这个,我觉得实用性太低。
2 楼 cloud21 2011-03-16  
很不错呢,加油。
1 楼 裴小星 2011-03-15  
OMSimpleBlog的代码有点小问题,Home类忘记加@Ignore注解了。
这个注解的作用是说明该Entity不会被持久化,因此不需要加载parent等。
不会导致错误,但会稍稍影响性能。

Master/user.html中
${name}

最好写作
<p>${name}</p>

以免没有文章时和提示信息混在一行中。


现在更新锁是直接用
if (forUpdate) {
    while (locks.contains(id)) {}
    locks.add(id);
}

进行检查的,jdk concurrent包中有一些现成的锁,
但更理想的是不使用锁,而对不同的更新进行merge。

相关推荐

    无线电能传输中电动汽车充电的Matlab与Maxwell仿真:线圈结构与补偿拓扑优化

    内容概要:本文详细介绍了无线电能传输技术在电动汽车充电中的应用,特别是在Matlab和Maxwell中的仿真过程。首先讨论了SS补偿拓扑的Matlab仿真,展示了如何设置线圈参数、进行谐振匹配以及通过相量分析判断软开关状态。接着探讨了Maxwell中DD线圈的3D电磁场仿真,强调了自定义网格划分和涡流场计算的重要性。随后,文章深入研究了多线圈阵列仿真,揭示了不同线圈布局对耦合系数的影响,并提出了LCC补偿拓扑的应用。此外,文中还分享了许多实用技巧,如避免常见错误、优化仿真参数以及处理实际测试中的问题。 适合人群:从事无线电能传输研究的技术人员、研究生及以上学历的研究人员。 使用场景及目标:适用于需要深入了解无线电能传输技术及其仿真的研究人员和技术开发者,旨在帮助他们掌握Matlab和Maxwell的具体应用,提高仿真精度和效率。 其他说明:文章不仅提供了详细的代码示例和仿真步骤,还分享了作者的实际经验和教训,使读者能够更好地理解和应对仿真过程中遇到的问题。

    用户增删改查功能的前端页面,添加了vue渲染代码

    用户增删改查功能的前端页面,添加了vue渲染代码。

    计算机课程设计相关资源

    计算机课程设计相关资源

    基于51单片机protues仿真的猜数字游戏(仿真图、源代码、AD原理图、流程图)

    基于51单片机protues仿真的猜数字游戏(仿真图、源代码、AD原理图、流程图) 猜数字游戏 1、通过随机数实现该游戏; 2、按下K1键启动游戏并随机生成一个0~9的数字 3、通过矩阵按键输入你的数字,输入数字小于随机生成的数字则显示小于该数,大于的时候显示大于该数,直到相等为止。 4、仿真图、源代码、AD原理图、流程图;

    基于MATLAB的FOC滑膜观测器与锁相环(MATLAB 2021b)实现及硬件移植注意事项

    内容概要:本文详细介绍了利用MATLAB 2021b搭建的FOC滑膜观测器(SMO)与锁相环(PLL)的仿真模型及其在M4硬件平台上的实现方法。文中首先展示了SMO的核心代码,解释了如何通过滑模面计算和符号函数处理来估算反电动势,并讨论了PLL用于速度提取的具体实现。接着探讨了仿真环境中直接0速闭环启动的效果以及实际硬件实现时所需的开环启动策略。此外,文章还分享了多个调试过程中遇到的问题及解决方案,如相位跳变、高频振荡、电流环参数调整等。 适合人群:从事电机控制研究的技术人员,尤其是对无感FOC感兴趣的工程师。 使用场景及目标:适用于希望深入了解FOC滑膜观测器和锁相环的工作原理并尝试将其应用于实际项目的开发者。目标是掌握SMO+PLL组合的设计思路和技术细节,同时了解硬件移植时需要注意的实际问题。 其他说明:文中提供了大量实用的代码片段和调试经验,对于想要快速入门或优化现有系统的读者非常有帮助。特别强调了仿真与现实之间的差异,提醒读者注意参数选择和滤波器设计等方面的不同之处。

    汽车美容员工手册.doc

    汽车美容员工手册.doc

    基于PSO算法的配电网分布式光伏选址定容优化及其Matlab实现

    内容概要:本文详细介绍了利用粒子群优化(PSO)算法解决配电网中分布式光伏系统的选址与定容问题的方法。首先阐述了问题背景,即在复杂的配电网环境中选择合适的光伏安装位置和确定合理的装机容量,以降低网损、减小电压偏差并提高光伏消纳效率。接着展示了具体的PSO算法实现流程,包括粒子初始化、适应度函数构建、粒子位置更新规则以及越界处理机制等关键技术细节。文中还讨论了目标函数的设计思路,将多个相互制约的目标如网损、电压偏差和光伏消纳通过加权方式整合为单一评价标准。此外,作者分享了一些实践经验,例如采用前推回代法进行快速潮流计算,针对特定应用场景调整权重系数,以及引入随机波动模型模拟光伏出力特性。最终实验结果显示,经过优化后的方案能够显著提升系统的整体性能。 适用人群:从事电力系统规划与设计的专业人士,尤其是那些需要处理分布式能源集成问题的研究人员和技术人员。 使用场景及目标:适用于希望深入了解如何运用智能优化算法解决实际工程难题的人士;旨在帮助读者掌握PSO算法的具体应用方法,从而更好地应对配电网中分布式光伏系统的选址定容挑战。 其他说明:文中提供了完整的Matlab源代码片段,便于读者理解和复现研究结果;同时也提到了一些潜在改进方向,鼓励进一步探索和创新。

    晋升考核制度.pptx

    晋升考核制度.pptx

    计网-主机发送IP数据报的过程思维导图

    计网-主机发送IP数据报的过程思维导图

    三菱FX3U PLC与三台E740变频器基于Modbus RTU通讯的工业自动化控制系统实现

    内容概要:本文详细介绍了三菱FX3U PLC与三台三菱E740变频器通过Modbus RTU协议进行通讯的具体实现方法。主要内容涵盖硬件配置(如PLC、变频器、触摸屏)、通讯参数设置(如波特率、数据位、校验方式)、PLC程序编写(包括初始化、启停控制、频率设定等)、触摸屏编程(如画面设计、变量关联)等方面。文中还分享了一些实际应用中的注意事项和避坑指南,确保通讯系统的稳定性和可靠性。 适用人群:从事工业自动化领域的工程师和技术人员,尤其是熟悉三菱产品和Modbus RTU协议的专业人士。 使用场景及目标:适用于需要实现PLC与多台变频器通讯的工业自动化项目,旨在提高系统的集成度和可控性,减少人工干预,提升生产效率。 其他说明:文中提供的实例和代码片段有助于读者快速理解和掌握相关技术要点,同时强调了实际操作中的常见问题及其解决方案。

    自动驾驶路径跟踪:基于二/三自由度动力学模型的MPC算法及Carsim-Simulink联合仿真

    内容概要:本文深入探讨了利用二/三自由度动力学模型和MPC(模型预测控制)实现自动驾驶车辆的任意路径跟踪技术。首先介绍了二自由度动力学模型的基本概念及其状态方程,随后详细解释了MPC的工作原理,包括目标函数的设计和优化求解过程。接着讨论了Carsim和Simulink联合仿真的具体实施步骤和技术要点,如采样同步、约束条件处理等。文中还分享了许多实用的工程经验和调试技巧,例如预瞄距离的设置、权重矩阵的选择以及如何应对高速工况下的挑战。最终通过仿真结果展示,证明了该方法的有效性和优越性。 适合人群:从事自动驾驶研究与开发的专业人士,尤其是对路径跟踪算法感兴趣的工程师和技术爱好者。 使用场景及目标:适用于需要精确路径跟踪的自动驾驶应用场景,旨在提高车辆行驶的安全性和效率。通过掌握本文介绍的方法和技术,可以帮助开发者更好地理解和实现基于MPC的路径跟踪系统。 其他说明:文章不仅提供了理论知识,还包括了大量的实战经验和代码片段,有助于读者快速上手并应用于实际项目中。同时强调了在不同速度范围选择合适自由度模型的重要性,为后续的研究和发展指明了方向。

    基于博途1200PLC的智能灌溉系统设计与实现 - 自动化农业应用

    内容概要:本文详细介绍了基于西门子S7-1200 PLC的智能灌溉系统的设计与实现。系统主要包括PLC控制器、触摸屏、传感器和执行机构。文中详细讲解了如何使用博途V16软件编写PLC程序,包括梯形图编程和SCL语言的应用,以及如何设计触摸屏监控画面。此外,还涉及了IO表和电气原理图的准备,确保系统的正确安装和维护。文章特别强调了自动灌溉的核心逻辑,如状态机结构和异常处理机制,以及触摸屏设计的小技巧,如动态图标和趋势图的使用。最后,提供了调试过程中的一些注意事项和优化建议。 适合人群:从事农业自动化领域的工程师和技术人员,尤其是熟悉PLC编程和触摸屏设计的专业人士。 使用场景及目标:适用于需要提高灌溉效率和精度的现代农业生产环境。目标是通过智能化控制减少水资源浪费,提升作物产量。同时,也为系统开发者提供了详细的实施指南和调试技巧。 其他说明:文章附带了完整的PLC程序、HMI界面和电气图纸,方便读者进行实际操作和验证。

    MINIQMT学习课程Day5

    国金qmt模拟客户端。 模拟账号密码,私聊。

    员工离职面谈记录表.doc

    员工离职面谈记录表.doc

    新员工关怀方案.doc

    新员工关怀方案

    轴承表面缺陷检测数据集解析:基于Python的图像处理与模型训练

    内容概要:本文详细介绍了轴承表面缺陷检测数据集的结构及其应用方法。数据集包含5824张高清轴承图像及其对应的XML标注文件,涵盖擦伤、凹槽、划痕三种类型的缺陷。作者通过Python代码展示了如何检查数据完整性、解析XML标注文件、进行数据可视化以及数据增强操作。此外,还讨论了使用YOLOv5和EfficientDet等模型进行缺陷检测的具体步骤和技术要点,强调了高分辨率图像处理和模型优化的方法。 适合人群:从事工业质检、机器视觉、深度学习等相关领域的研究人员和工程师。 使用场景及目标:适用于需要处理高分辨率工业图像并进行缺陷检测的研究和工程项目。主要目标是提高缺陷检测的准确性,特别是在复杂的工业环境中。 其他说明:文中提供了大量实用的Python代码片段,涵盖了从数据预处理到模型训练的各个环节。特别提到了针对金属表面反光、多缺陷共存等问题的技术解决方案。

    招聘甘特图.xlsx

    招聘甘特图.xlsx

    招聘仪表盘构建及为数据解读P11.pptx

    招聘仪表盘构建及为数据解读P11.pptx

    基于MATLAB的光纤通信物理层传输算法仿真与调试技巧

    内容概要:本文详细介绍了利用MATLAB进行光纤通信物理层传输算法仿真的方法和技术要点。主要内容涵盖色散补偿、非线性放大器建模、信号重构、时钟恢复、QPSK调制、误码率分析等方面。文中提供了多个具体的MATLAB代码示例,如色散补偿、非线性放大器特性拟合、QPSK调制与解调、眼图生成等,并分享了许多调试经验和常见问题解决方案。此外,作者还强调了仿真过程中需要注意的关键细节,如参数设置、变量监控、噪声注入方式等。 适合人群:从事光纤通信研究的技术人员、研究生以及对通信系统仿真感兴趣的开发者。 使用场景及目标:适用于希望深入了解光纤通信物理层传输机制及其仿真实现的研究人员和工程师。目标是帮助读者掌握MATLAB在通信仿真中的应用,提高仿真效率并减少调试时间。 其他说明:文章不仅提供详细的代码示例,还分享了大量实战经验,有助于读者快速上手并解决实际问题。

    线性代数_李宏毅_视频笔记_学习辅助_1742822508.zip

    线性代数

Global site tag (gtag.js) - Google Analytics