import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.io.FileUtils;
import com.bcap_systemlib.config.Configuration;
public class FileUtil {
/**
* 将多个文件打包到一个zip中
*
* @param sourceFolder
* @param zipFile
* @return
* @throws Exception
*/
public static boolean zipFile(String sourceFolder, File zipFile) throws Exception{
boolean isOk = true;
File f = new File(sourceFolder);
ZipOutputStream out = null;
try{
if(!f.exists()){
f.mkdirs();
}
out = new ZipOutputStream(new FileOutputStream(zipFile));
zip(out, f, "");
out.flush();
FileUtils.deleteDirectory(f);
} catch (Exception e){
e.printStackTrace();
throw new Exception("压缩文件出错!");
} finally
{
if(null != out){
try{ out.close(); } catch (Exception e){ e.printStackTrace();}
}
}
return isOk;
}
/**
* 递归压缩文件
* @param out
* @param f
* @param base
* @throws Exception
*/
private static void zip(ZipOutputStream out, File f, String base) throws Exception {
if (f.isDirectory()) {
File[] fl = f.listFiles();
out.putNextEntry(new ZipEntry(base + "/"));
base = base.length() == 0 ? "" : base + "/";
for (int i = 0; i < fl.length; i++) {
zip(out, fl[i], base + fl[i].getName());
}
}else {
out.putNextEntry(new ZipEntry(base));
FileInputStream in = new FileInputStream(f);
int b;
while ( (b = in.read()) != -1) {
out.write(b);
}
in.close();
}
}
/**
* 下载单个文件
*
* @param file
* @param request
* @param response
* @return
*/
public static boolean downFile(File file, HttpServletRequest request, HttpServletResponse response) {
boolean isOk = true;
OutputStream myout = null;
FileInputStream fis = null;
BufferedInputStream buff = null;
HttpSession session = request.getSession();
if (session != null) {
session.setAttribute("state", "");
}
try {
response.setContentType("application/x-msdownload");
response.setContentLength((int) file.length());
response.setHeader("content-disposition", "attachment;filename=" + EncodingConvertUtil.utf2iso(file.getName()));
fis = new FileInputStream(file);
buff = new BufferedInputStream(fis);
byte[] b = new byte[1024 * 10];//相当于我们的缓存
long k = 0;//该值用于计算当前实际下载了多少字节
//从response对象中得到输出流,准备下载
myout = response.getOutputStream();
while (k < file.length()) {
int j = buff.read(b, 0, b.length);
k += j;
//将b中的数据写到客户端的内存
myout.write(b, 0, j);
}
myout.flush();
} catch (Exception e) {
e.printStackTrace();
isOk = false;
} finally {
try {
if (null != myout) {
myout.close();
myout = null;
}
if (null != buff) {
buff.close();
buff = null;
}
if (null != fis) {
fis.close();
fis = null;
}
if(file.exists()){
FileUtil.delFile(file);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return isOk;
}
/**
* 删除单个文件
*
* @param file
* @return
*/
public static boolean delFile(File file) {
boolean isOk = true;
try {
if (file.isFile() && file.exists()) {
file.delete();
}
} catch (Exception e) {
e.printStackTrace();
isOk = false;
} finally {
// log ...
}
return isOk;
}
public static void downloadFileFromRemote(String remoteFilePath, String localFilePath){
URL urlfile = null;
HttpURLConnection httpUrl = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
File f = new File(localFilePath);
try
{
urlfile = new URL(remoteFilePath);
httpUrl = (HttpURLConnection)urlfile.openConnection();
httpUrl.connect();
bis = new BufferedInputStream(httpUrl.getInputStream());
bos = new BufferedOutputStream(new FileOutputStream(f));
int len = 2048;
byte[] b = new byte[len];
while ((len = bis.read(b)) != -1)
{
bos.write(b, 0, len);
}
System.out.println("下载成功");
bos.flush();
bis.close();
httpUrl.disconnect();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
bis.close();
bos.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
public static void writeFile(String filePathAndName, String fileContent) {
try {
File f = new File(filePathAndName);
if (!f.exists()) {
f.createNewFile();
}
OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(f),Configuration.SERVLET_CHARACTER_ENCODING);
BufferedWriter writer=new BufferedWriter(write);
writer.write(fileContent);
writer.close();
} catch (Exception e) {
System.out.println("写文件内容操作出错");
e.printStackTrace();
}
}
public static String readFile(String filePathAndName) {
String fileContent = "";
try {
File f = new File(filePathAndName);
if(f.isFile()&&f.exists()){
InputStreamReader read = new InputStreamReader(new FileInputStream(f),Configuration.SERVLET_CHARACTER_ENCODING);
BufferedReader reader=new BufferedReader(read);
String line;
while ((line = reader.readLine()) != null) {
fileContent += line;
}
read.close();
}
} catch (Exception e) {
System.out.println("读取文件内容操作出错");
e.printStackTrace();
}
return fileContent;
}
public static String getExtName(String filename) {
int index = filename.lastIndexOf(".");
if (index == -1) {
return null;
}
String result = filename.substring(index);
return result;
}
}
flutter dart md5
flutter dart好像不像php那样,直接提供md5函数,因此从网上找了一段,记录一下
import 'dart:convert';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
class MD5{
String generateMd5(String data) {
var content = new Utf8Encoder().convert(data);
var digest = md5.convert(content);
return hex.encode(digest.bytes);
}
}
flutter dart上的http请求类
从网上找到了一个flutter dart请求类,但post部分有错误,已经做了修改,现贴出来
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:blue_collar_age_class/utils/config.dart';
class HttpUtil {
static HttpUtil? instance;
Dio? dio;
BaseOptions? options;
CancelToken cancelToken = CancelToken();
static HttpUtil? getInstance() {
if (null == instance) instance = HttpUtil();
return instance;
}
/*
* config it and create
*/
HttpUtil() {
//BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
options = BaseOptions(
//请求基地址,可以包含子路径
baseUrl: Config.URL_BASE,
//连接服务器超时时间,单位是毫秒.
connectTimeout: 10000,
//响应流上前后两次接受到数据的间隔,单位为毫秒。
receiveTimeout: 5000,
//Http请求头.
headers: {
//do something
"version": "1.0.0"
},
//请求的Content-Type,默认值是"application/json; charset=utf-8",Headers.formUrlEncodedContentType会自动编码请求体.
contentType: Headers.formUrlEncodedContentType,
//表示期望以那种格式(方式)接受响应数据。接受四种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
responseType: ResponseType.plain,
);
dio = Dio(options);
//Cookie管理
dio!.interceptors.add(CookieManager(CookieJar()));
//添加拦截器
dio!.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) {
print("请求之前");
// Do something before request is sent
return options; //continue
}, onResponse: (Response response) {
print("响应之前");
// Do something with response data
return response; // continue
}, onError: (DioError e) {
print("错误之前");
// Do something with response error
return e; //continue
}));
}
/*
* get请求
*/
get(url, {data, options, cancelToken}) async {
Response? response;
try {
response = await dio!.get(url,
queryParameters: data, options: options, cancelToken: cancelToken);
print('get success---------${response.statusCode}');
print('get success---------${response.data}');
// response.data; 响应体
// response.headers; 响应头
// response.request; 请求体
// response.statusCode; 状态码
} on DioError catch (e) {
print('get error---------$e');
formatError(e);
}
return response;
}
/*
* post请求
*/
post(url, {data, params, options, cancelToken}) async {
Response? response;
try {
response = await dio!.post(url, data:data, queryParameters: params, options: options, cancelToken: cancelToken);
print('post success---------${response.data}');
} on DioError catch (e) {
print('post error---------$e');
formatError(e);
}
return response;
}
/*
* 下载文件
*/
downloadFile(urlPath, savePath) async {
Response? response;
try {
response = await dio!.download(urlPath, savePath,
onReceiveProgress: (int count, int total) {
//进度
print("$count $total");
});
print('downloadFile success---------${response.data}');
} on DioError catch (e) {
print('downloadFile error---------$e');
formatError(e);
}
return response!.data;
}
/*
* error统一处理
*/
void formatError(DioError e) {
if (e.type == DioErrorType.CONNECT_TIMEOUT) {
// It occurs when url is opened timeout.
print("连接超时");
} else if (e.type == DioErrorType.SEND_TIMEOUT) {
// It occurs when url is sent timeout.
print("请求超时");
} else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
//It occurs when receiving timeout
print("响应超时");
} else if (e.type == DioErrorType.RESPONSE) {
// When the server response, but with a incorrect status, such as 404, 503...
print("出现异常");
} else if (e.type == DioErrorType.CANCEL) {
// When the request is cancelled, dio will throw a error with this type.
print("请求取消");
} else {
//DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null.
print("未知错误");
}
}
/*
* 取消请求
*
* 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。
* 所以参数可选
*/
void cancelRequests(CancelToken token) {
token.cancel("cancelled");
}
}
AirTrack控制FF777,应答机问题
使用AirTrack控制FF777时,修改应答机,会发现机模上的应答机数值不变,此时,需要打开菜单,将Radio plugin打开,使用外部设备控制,机模应答机出现字符“E”,使用AirTrack可正常控制。
AirTrack简介,及踩过的坑
为了使模拟飞行变得更加方便有趣,往往会使用一些外置设备,今天介绍一下“软外设”——AirTrack
AirTrack可以在苹果应用商店下载,228元,打开之后是下图的样子,需要去官网上下载电脑端插件,安装到X-Plane 11插件目录中,设定一下IP地址,当AirTrack和X- Plane 11处于同一局域网时,即可自动显示主机,点击主机连接。
默认的导航数据是2001版本的,很旧,最新版的又仅支持美国数据,想要最新全球数据,需要订阅navigraph,将其AirTrack的订阅码复制到本软件中,navigraph订阅208元/年(当前美元人民币汇率),下图是一些配置,主要是数值单位的选择,可以在国际单位和英制单位中切换。
按下CHARTS,可以查看航图,坑来了,默认是一些demo,还有本地航图,外部航图选项,外部航图要30元,我买了,买完之后才发现:US only,白白被坑¥30,如果需要航图的话,可以去网上找Jeppesen航图(for free),通过数据线同步到本软件中。
通讯这还是不错的,点击相应的内容,可以直接修改,省去了旋转旋钮的麻烦,尤其是在连飞活动中,这可以有效缓解手忙脚乱。
自动驾驶面板也不错,简单到777,复杂到737,全部功能都包含在里面了,这里说明一下,737中的LVL CHG和777中的FLCH是一样的。
然后是fmc,如果你用的是X-Plane 11自带的民航机,fmc信息会自动同步到这里,若是插件机,FF777,zibo 738之类的,则无法自动同步,想要使用此功能,需要先编辑好航路信息,或者用自带的机模将航路数据同步进来,然后再SAVE一下,再加载插件机。
最后就是仪表这,这个地方也是很细致,放大和缩小倍数那里比机模更细致。
总的来说,两个坑,一个是航路数据导入,一个是千万不要购买航图。
航天飞机冲出跑道,好像看见了首都机场线地铁
简单的连飞服务器搭建
据我了解,X-Plane 11只支持本地局域网联机,不支持公网联机,想要公网联机,通常需要在连飞平台上注册呼号,借助连飞客户端登录连飞平台,实现联机,常见的连飞平台有XFlySim,CFR(航空人生),VATSIM等,本文介绍,如何自己搭建连飞服务器,公网内网都可以用。支持X- Plane 11,FSX和P3D
准备材料
去github找到FSD代码,链接:https://github.com/sparrowhe/fsd
服务器:Linux
服务端编译运行
将代码包上传到Linux服务器并解压好,进入到CMakeLists.txt所在目录,使用cmake相关命令编译输出可执行程序fsd,编辑cert.txt,第一列是呼号,第二列是密码,此文件记录了连飞服务器的会员注册信息,执行./fsd启动服务器,别忘了放通相关端口(6809,3011,3010)。
这里面有个细节,就是文件编码,可以使用vim查看一下,默认应该是dos,需要用dos2unix工具转换成unix的。
安装客户端
下载连飞客户端xswift,及CSL机模映射包,安装好xswift后,根据提示一步一步配置,启动X-Plane 11,连接服务器,此时,多人可上线编队飞行
管制端
管制端软件使用EuroScope,安装好后,可以直接连接FSD服务器,管制扇区可以去VATSIM找。
结束
至此,能连飞,能管制的连飞系统搭建完成
本教程写的相当简单,起个提示作用,因为我相信,能有想法做连飞的,都不是吃干饭的。
Jeppesen航图安装的坑
在飞行宝上找到Jeppesen的安装程序,资源详情页有安装教程,但安装时要注意,只能安装在C盘,但是又不想安装在C盘,怎么办呢?答:在D、E或者F盘创建Jeppesen的安装目录,创建快捷方式到C盘,这样在安装的时候,表面上看是安装到了C盘,实际安装到了其他盘
我是安装了N次才成功,第一次,安装完了打不开,百度告诉我需要管理员权限,给了管理员权限也不行,有人说不能用教育版本操作系统,又改成了家庭版,还是不行,最后按照默认路径安装到C盘成功了,但C盘满了,干脆卸掉,创建快捷方式后,重新安装。
X-Plane 11常用插件
AVS——机场视觉系统,起个实时定位作用,能方便的知道自己在哪,该怎么走
Airport-Navigator——机场导航,D版的AVS有时有BUG,无法正常使用,这个用起来不如AVS方便,但能在AVS失效时顶上
AviTab——飞行平板,能查航路,看地图,在B738中,能叫地勤
BetterPushback——后推车,用于飞机的后推
GndHandling——地勤插件
Little Xpconnect——连接little navmap,起连飞地图的作用
SAM——地面引导车,动态桥廊,等等
TerrainRadar——地形雷达
WebFMC——用手机平板控制FMC
X-Camera——视角切换
XPUIPC——连接SIMACARS,用于虚拟民航
xswiftbus——连接FSD服务器,用于连飞
FF777 插件机
FF777插件机初次启动只有很少的油,加油其他插件机复杂一些,打开菜单,设定乘客数量、行李重、APU时间、滑行时间、起飞油量,打开Doors MAN、舷梯、油车、电车、轮挡、打开全部机舱门,点击LOAD FUEL开始加油,等待加油完成即可,不支持空中修改油量。
相比于B738,没有了导航频率和此航线的旋钮,盲降设置在FMC的NAV RAD(导航雷达)里面了,在ILS-MLS处设置导航台频率和磁航向。
俯仰配评轮没了,鼠标也不好使,需要使用快捷键,襟翼和扰流板也需要快捷键。
自动驾驶面板中,FLCH相当于B738的LVL CHG,A/P是CMD,SEL都设在了旋钮中心
FF777自动化程度很高,会B738之后,这个几乎是零难度上手