0%

依赖包

  • FreeSpire.XLS

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using Spire.Xls;
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PrintPreviewTest
{
class Program
{
static void Main(string[] args)
{
//打印参数对话框
PrintDialog printdialog = new PrintDialog();

//使用Spire加载文档
Workbook workbook = new Workbook();
workbook.LoadFromFile(args[0]);

//预设参数
Worksheet sheet = workbook.Worksheets[0];
//设置打印纸张大小
sheet.PageSetup.PaperSize = PaperSizeType.PaperA4;
//设置打印方向
sheet.PageSetup.Orientation = PageOrientationType.Portrait;
//设置打印区域
sheet.PageSetup.PrintArea = "B2:F8";
//打印标题
sheet.PageSetup.PrintTitleColumns = "$A:$B";
sheet.PageSetup.PrintTitleRows = "$1:$2";
//打印顺序
sheet.PageSetup.Order = OrderType.DownThenOver;
sheet.PageSetup.Order = OrderType.OverThenDown;
//设置打印对话框属性
PrintDialog dialog = new PrintDialog();
dialog.AllowPrintToFile = true; dialog.AllowCurrentPage = true;
dialog.AllowSomePages = true;
//设置单面打印
dialog.PrinterSettings.Duplex = Duplex.Simplex;
//设置打印页面范围
dialog.PrinterSettings.FromPage = 0;
dialog.PrinterSettings.ToPage = 8;
dialog.PrinterSettings.PrintRange = PrintRange.SomePages;
//设置打印份数
dialog.PrinterSettings.Copies = 5;
//设置打印机名称
dialog.PrinterSettings.PrinterName = "HP LasterJet P1007";


//关联
printdialog.Document = workbook.PrintDocument;

if(printdialog.ShowDialog() == DialogResult.OK)
{
//根据对话框重设参数
workbook.PrintDocument.PrinterSettings = printdialog.PrinterSettings;
PrintPreviewDialog previewDialog = new PrintPreviewDialog();
previewDialog.Document = workbook.PrintDocument;
previewDialog.ShowDialog();
}
}
}
}

预览

Yield语法糖

Yield是一个C#的语法糖,简化了诸如下面的方法,在返回迭代器接口类型IEnumerable<T>的函数中可以简化迭代器接口对象的创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//简化前
private static IEnumerable<string> GetCollByCommon()
{
List<string> list = new List<string>();
list.Add("4");
list.Add("5");
list.Add("6");
return list;
}

//简化后
private static IEnumerable<string> GetCollByYield()
{
yield return "1";
yield return "2";
yield return "3";
}

需要注意的是此函数的运行时机,采用以下代码测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void Main(string[] args)
{
Console.WriteLine("Test is beginning!");
IEnumerable<string> yield = GetCollByYield();
Console.WriteLine("Loop is beginning!");
foreach (var p in yield)
Console.WriteLine(p);
}


private static IEnumerable<string> GetCollByYield()
{
Console.WriteLine("GetCollByYield method is called!");
yield return "1";
yield return "2";
yield return "3";
}

运行结果如下,说明在使用yield关键字的方法中方法总是在使用方法的返回结果时才会被调用。

1
2
3
4
5
6
Test is beginning!
Loop is beginning
GetCollByYield method is called!
1
2
3

另外使用yield还有一些注意事项:
你不能在具有以下特点的方法中包含 yield return 或 yield break 语句:
匿名方法。 有关详细信息,请参阅匿名方法(C# 编程指南)。
包含不安全的块的方法。 有关详细信息,请参阅unsafe(C# 参考)。
异常处理
不能将 yield return 语句置于 try-catch 块中。 可将 yield return 语句置于 try-finally 语句的 try 块中。
yield break 语句可以位于 try 块或 catch 块,但不能位于 finally 块。
如果 foreach 主体(在迭代器方法之外)引发异常,则将执行迭代器方法中的 finally 块。

线程

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ThreadTest{
public static readonly object locker = new object();
public void FuncWithParameter(object obj){
lock(locker){
Console.WriteLint(obj);
}
}
public void FuncWithoutParameter(){
lock(locker){
Console.WriteLine("无参方法")
}
}
}
public class Program{
static void Main(string[] args){
ThreadTest test = new ThreadTest();
Thread threadWithParameter = new Thread(new ParameterizedThreadStart(test.FuncWithParameter));
Thread threadWithoutParameter = new Thread(new ThreadStart(test.FuncWithoutParameter));
threadWithParameter.Start("有参方法");
threadWithoutParameter.Start();
}
}

线程方法

方法 描述 执行后状态 -
Start 启动线程
Suspend 挂起线程
Resume 继续已挂起的线程
Interrupt 终止处于WaitSleepJoin线程状态的线程
Join 阻塞调用线程,直到某个线程终止时为止
Sleep 将当前线程阻塞指定的毫秒数
Abort 终止线程

线程状态

状态 描述 - -
Aborted 线程已停止;
AbortRequested 线程的Thread.Abort()方法已被调用,但是线程还未停止;
Background 线程在后台执行,与属性Thread.IsBackground有关;
Running 线程正在正常运行;
Stopped 线程已经被停止;
StopRequested 线程正在被要求停止;
SuspendRequested 线程正在要求被挂起,但是未来得及响应;
Unstarted 未调用Thread.Start()开始线程的运行;
WaitSleepJoin 线程因为调用了Wait(),Sleep()或Join()等方法处于封锁状态;

通过使用AutoResetEvent和ManualResetEvent类来控制循环线程的暂停与继续 保证了每次循环的完整性。

简单来说只有调用Set()方法后才能执行WaitOne()后面的代码,AutoResetEvent和ManualResetEvent分别都有Set()改变为有信号 ,Reset()改变为无信号,WaitOne()将会阻塞当前调用的线程,直到有信号为止,即执行了Set()方法,WaitOne()方法还可以带指定时间的参数。

理解了AutoResetEvent后再理解ManualResetEvent也就不难了,AutoResetEvent与ManualResetEvent的区别是,AutoResetEvent.WaitOne()会自动改变事件对象的状态,即AutoResetEvent.WaitOne()每执行一次,事件的状态就改变一次,也就是从无信号变为有信号,或从有信号变为无信号。而ManualResetEvent则是调用Set()方法后其信号量不会自动改变,除非再设置Reset()方法。

在.Net多线程编程中,AutoResetEvent和ManualResetEvent这两个类经常用到, 他们的用法很类似,但也有区别。Set方法将信号置为发送状态,Reset方法将信号置为不发送状态,WaitOne等待信号的发送。可以通过构造函数的参数值来决定其初始状态,若为true则非阻塞状态,为false为阻塞状态。如果某个线程调用WaitOne方法,则当信号处于发送状态时,该线程会得到信号, 继续向下执行。其区别就在调用后,AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待.也就是说,AutoResetEvent一次只唤醒一个线程;而ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。也就是说,除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Program{
//ManualResetEvent
static AutoResetEvent autoResetEvent = new AutoResetEvent(false);//初始设置为false为默认阻塞线程
static void FuncAutoResetEvent(){
autoResetEvent.WaitOne();//等待autoResetEvent的Set信号,执行后状态自动设置为false,保证只有一个进程进入,而ManualResetEvent激活后不再设回false,可以同时唤醒多个线程。
Console.WriteLine("线程继续执行.......");
}
static void Main(string[] args){
Thread thread = new Thread(new ThreadStart(FuncAutoResetEvent));
thread.Start();
ConsoleKeyInfo key = Console.ReadKey();
if(key.Key == ConsoleKey.Y){
autoResetEvent.Set();
}
}
}

C#强制要求代码是线程安全的,即不允许跨线程访问Windows窗体的控件

1
2
3
Action<int> setVal = (i) => {this.myTxtBox.Text == i.ToString()};
this.myTxtBox.Invoke(setVal,i);
//Control.Invoke(Delegate method,params object[] args) 在拥有控件的基础窗口句柄的线程上,用指定的自变量列表执行指定委托。

.NET三种异步编程模式

异步编程模型(APM)

概念

  1. .NET 1.0提出了APM(Asynchronous Programming Model)即异步编程模式。
    .NET的类库有以BeginXXX和EndXXX类似的方法,就是使用异步编程模型。
  2. NET Framework很多类也实现了该模式,同时我们也可以自定义类来实现该模式,即在自定义的类中实现返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法,另外委托类型也定义了BeginInvoke和EndInvoke方法。
    异步编程模型的本质
  3. 利用委托和线程池帮助我们实现异步编程模型模式。
  4. 该模式利用一个线程池线程去执行一个操作,在FileStream类BeginRead方法中就是执行一个读取文件操作,该线程池线程会立即将控制权返回给调用线程,此时线程池线程在后台进行这个异步操作;
  5. 异步操作完成之后,通过回调函数来获取异步操作返回的结果,此时就是利用委托的机制。

借助委托来实现APM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
AsyncCount count = new AsyncCount(Program.count);
IAsyncResult result = count.BeginInvoke(1, 100, out int threadId, null, null);
Thread.Sleep(1000);
Console.WriteLine("回到主线程");
Console.WriteLine("开始等待子线程");
int cou = count.EndInvoke(out threadId, result);
Console.WriteLine($"结果是{cou}");
}
public static int count(int start, int end, out int threadId)
{
Console.WriteLine("进入子线程");
int count = 0;
for (int i = start; i < end; i++)
{
Thread.Sleep(100);
count += i;
}
threadId = Thread.CurrentThread.ManagedThreadId;
return count;
}
public delegate int AsyncCount(int start, int end, out int threadId);
}

基于事件的异步模式(EAP)

Event-based Asynchronous Pattern(EAP),基于事件的异步模式,实际上就是提供了完成任务事件的类,供外部订阅来,实现异步。

还拿数数这个模型来表示一下基于事件的异步模式类。

1
2
3
4
5
6
7
public class CountModel
{
public int Count(int start,int end);
public void CountAsync(int start,int end);
public void CountAsync(int start,int end,object userState);
public event CountCompletedEventHandler CountCompleted;
}

下面为具体的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class CountModel
{
public int Count(int start,int end)
{
Console.WriteLine("同步线程开始");
int count = 0;
for (int i = start; i < end; i++)
{
Thread.Sleep(1000);
count += i;
}
Console.WriteLine("同步线程完成");

return count;
}
public void CountAsync(int start,int end,object userState)
{
Thread thread = new Thread(new ThreadStart(() =>
{
Console.WriteLine("异步线程开始");
int count = 0;
for (int i = start; i < end; i++)
{
Thread.Sleep(1000);
count += i;
}
CountCompleted?.Invoke(this, new CountCompletedEventArgs() { Result = count });
Console.WriteLine("异步线程完成");
}));
thread.Start();
}
public event CountCompletedEventHandler CountCompleted;
public delegate void CountCompletedEventHandler(object sender, CountCompletedEventArgs args);
public class CountCompletedEventArgs
{
public int Result;
}
}

基于任务的异步模式(TAP)

Task-based Asynchronous Pattern(TAP)

TAP与EAP的不同在于,EAP中异步方法返回类型为void,而TAP的返回类型为可等待类型如(Task、Task、ValueTask 和 ValueTask),线程结束后不再通过触发完成事件,二是通过立即返回的Task中获取结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CountModel
{
public Task<int> CountAsync(int start,int end,object userState)
{
return Task.Run(()=>{
int count = 0;
for (int i = start; i < end; i++)
{
Thread.Sleep(1000);
count += i;
}
return count;
})
}
}

Task库的使用

优越性:Task >> ThreadPool >> Thread

async/await

用async来修饰一个方法,表明这个方法是异步的,声明的方法的返回类型必须为:void或Task或Task。方法内部必须含有await修饰的方法,如果方法内部没有await关键字修饰的表达式,哪怕函数被async修饰也只能算作同步方法,执行的时候也是同步执行的。

被await修饰的只能是Task或者Task类型,通常情况下是一个返回类型是Task/Task的方法,当然也可以修饰一个Task/Task变量,await只能出现在已经用async关键字修饰的异步方法中。上面代码中就是修饰了一个变量ResultFromTimeConsumingMethod。

三种async异步方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//第一种返回Task,可等待
public async Task MethodAsync(int input){
await Task.Run(()=>{
Console.WriteLine(input);
})
}
//第二种返回Task<TResult>,可等待
public async Task<int> MethodAsync(int input){
return await Task.Run(()=>{
return input;
})
}
//第三种无返回,不可等待
public async void MethodAsync(int input){
await Task.Run(()=>{
Console.WriteLine(input)
})
}
  • 异步方法的返回类型:void Task Task,后两者为可等待方法。
  • 可等待方法如果不进行等待,VS会进行提示。
  • 可等待方法需要使用var task = MethodAsync()来获取结果或者等待,或者使用await标记形成新的异步方法。
  • 必须成对出现
  • await关键字为两线程的分割点,await关键词前的语句在主线程执行,后面另起新线程执行。

调用控制台应用程序

1
2
3
4
5
6
7
8
9
10
11
Process p = new Process();
p.StartInfo.FileName = "cmd.exe"; //调用的exe
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInp= true;
p.StartInfo.RedirectStandardOutp= true;
p.StartInfo.CreateNoWindow = false;
p.Start();
p.StandardInput.WriteLine("cd "rootPath);
//等待执行结束
p.WaitForExit();

C#调用exe和双击exe文件的区别

程序运行时,有个 WorkingDirectory 的概念。
你的FORM调用别的EXE时,如果EXE和你的FORM程序,不在一个目录,那么,那个EXE默认是认为你的目录是它的所在目录,这样的情况下,如果那个EXE有配置文件需要读取的话,就读取不到了。所以,在你调用那个EXE时,指定好那个EXE,WorkingDirectory为该EXE所在目录,即可解决问题。

要重定向 IO 流,Process 对象必须将 UseShellExecute 属性设置为 False。

第三方调用cmd 与 常规打开cmd 的区别

这就是因为当年的DOS现在已经不跟大家说导致的一些信息缺失导致的问题了。
首先我们说一个CMD窗口也就是一个command .com(dos)启动后他是有一些默认配置信息的:
path就是其中之一,如果不配置path那么所有的外部命令(command内置一些内部命令,除此之外的exe、com、bat等都是外部命令)都必须输入全部路径才能正确访问。

然后我们说说windows不论你运行还是开始菜单点击cmd,都是默认加载系统配置中的path的,配置位置见下图:

但是当你使用第三方程序启动的cmd,就不会加载这些默认信息了。
所以就这么简单,你要么在加载cmd的时候赋予path,要么使用绝对路径调用外部命令。

第三方调用CMD时赋予环境变量

1
Environment.SetEnvironmentVariable("","");

有些常规打开一闪而过的程序,一般需要设置参数使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class LongArray<T> : IDisposable where T : struct
{
private IntPtr _head;
private Int64 _capacity;
private UInt64 _bytes;
private Int32 _elementSize;

public LongArray(long capacity)
{
if (_capacity < 0) throw new ArgumentException("The capacity can not be negative");
_elementSize = SizeOf(default(T));
_capacity = capacity;
_bytes = (ulong)capacity * (ulong)_elementSize;
_head = AllocHGlobal((IntPtr)_bytes);
}

public T this[long index]
{
get
{
IntPtr p = _getAddress(index);
T val = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(T));
return val;
}
set
{
IntPtr p = _getAddress(index);
StructureToPtr(value, p, true);
}
}

protected bool disposed = false;
public void Dispose()
{
if (!disposed)
{
FreeHGlobal((IntPtr)_head);
disposed = true;
}
}

public IntPtr _getAddress(long index)
{
if (disposed)
throw new ObjectDisposedException("Can't access the array once it has been disposed!");
if (index < 0)
throw new IndexOutOfRangeException("Negative indices are not allowed");
if (!(index < _capacity))
throw new IndexOutOfRangeException("Index is out of bounds of this array");
return (IntPtr)((ulong)_head + (ulong)index * (ulong)(_elementSize));
}
}

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#查看容器状态
docker stats
#使用镜像启动容器并进入容器
docker run -it ubuntu /bin/bash
#查看所有容器
docker ps -a
#查询运行的容器
docker ps
#或者
docker container ls
#启动一个已停止的容器
docker start <container_id>
#后台运行
docker run -itd --name ubuntu-test ubuntu /bin/bash
#停止容器
docker stop <container_id>
#停止的容器可以通过 docker restart 重启
docker restart <container_id>
#进入容器
docker exec -it <container_id> /bin/bash
#导出容器快照
docker export <container_id> > ubuntu.tar
#保存容器
docker commit <container_id> <image_name>
#创建镜像
docker build -t blog .
#复制文件到容器
docker cp . <container_id>:/home
#保存镜像到本地
docker save -o backup.tar <image>
#加载镜像到Docker
docker load --input backup.tar
#Docker内不能运行服务-以特权模式运行容器
docker run -d --name centos7 --privileged=true centos:7 /usr/sbin/init
#清理不带标签的镜像
docker rmi -f $(docker images |grep "<none>" | awk "{print \$3}")

docker run 命令参数

语法
docker run [OPTIONS] IMAGE [COMMAND] [ARG…]

  • -a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
  • -d: 后台运行容器,并返回容器ID;
  • -i: 以交互模式运行容器,通常与 -t 同时使用;
  • -P: 随机端口映射,容器内部端口随机映射到主机的端口
  • -p: 指定端口映射,格式为:主机(宿主)端口:容器端口
  • -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
  • –name=“nginx-lb”: 为容器指定一个名称;
  • –dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;
  • –dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;
  • -h “mars”: 指定容器的hostname;
  • -e username=“ritchie”: 设置环境变量;
  • –env-file=[]: 从指定文件读入环境变量;
  • –cpuset=“0-2” or --cpuset=“0,1,2”: 绑定容器到指定CPU运行;
  • -m :设置容器使用内存最大值;
  • –net=“bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
  • –link=[]: 添加链接到另一个容器;
  • –expose=[]: 开放一个端口或一组端口;
  • –volume , -v: 绑定一个卷

docker build 命令参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[root@iCyrene ~]# docker build --help

Usage: docker build [OPTIONS] PATH | URL | -

Build an image from a Dockerfile

Options:
--add-host list Add a custom host-to-IP mapping (host:ip)
--build-arg list Set build-time variables
--cache-from strings Images to consider as cache sources
--cgroup-parent string Optional parent cgroup for the container
--compress Compress the build context using gzip
--cpu-period int Limit the CPU CFS (Completely Fair Scheduler) period
--cpu-quota int Limit the CPU CFS (Completely Fair Scheduler) quota
-c, --cpu-shares int CPU shares (relative weight)
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1)
--cpuset-mems string MEMs in which to allow execution (0-3, 0,1)
--disable-content-trust Skip image verification (default true)
-f, --file string Name of the Dockerfile (Default is 'PATH/Dockerfile')
--force-rm Always remove intermediate containers
--iidfile string Write the image ID to the file
--isolation string Container isolation technology
--label list Set metadata for an image
-m, --memory bytes Memory limit
--memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable unlimited swap
--network string Set the networking mode for the RUN instructions during build (default "default")
--no-cache Do not use cache when building the image
--pull Always attempt to pull a newer version of the image
-q, --quiet Suppress the build output and print image ID on success
--rm Remove intermediate containers after a successful build (default true)
--security-opt strings Security options
--shm-size bytes Size of /dev/shm
-t, --tag list Name and optionally a tag in the 'name:tag' format
--target string Set the target build stage to build.
--ulimit ulimit Ulimit options (default [])

创建MySql容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#设置镜像加速
#拉取镜像
docker pull mysql
#创建容器
#-p 代表端口映射,格式为 宿主机映射端口:容器运行端口
#-e 代表添加环境变量 MYSQL_ROOT_PASSWORD是root用户的登陆密码
docker run -di --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql
#进入容器
docker exec -it mysql /bin/bash
#登录Mysql
mysql -u root -p
#查看状态
mysql> status;
#进行授权远程连接(注意mysql 8.0跟之前的授权方式不同)
mysql> GRANT ALL ON *.* TO 'root'@'%';
#刷新权限
mysql> flush privileges;
#此时,还不能远程访问,因为Navicat只支持旧版本的加密,需要更改mysql的加密规则
#更改加密规则
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER;
#更新root用户密码
mysql> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
#刷新权限
mysql> flush privileges;
#退出数据库
mysql> exit;
#退出容器
1
2
3
4
5
6
7
8
9
10
11
12
docker pull mysql

docker run -di --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql
docker exec -it mysql /bin/bash
mysql -u root -p

GRANT ALL ON *.* TO 'root'@'%';
flush privileges;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER;
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
flush privileges;
exit;
1
2
#docker mysql5.7
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 mysql:5.7

windows宿主机与虚拟机中的容器连接

1
#添加宿主机到Docker的路由

本机安装

1
2
3
4
5
6
7
8
9
10
11
12
# 安装java
yum install -y java
# 添加Jenkins库到yum库,Jenkins将从这里下载安装。
wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
#安装
yum install -y jenkins
#配置端口
vi /etc/sysconfig/jenkins
JENKINS_PORT="8080" #此端口不冲突可以不修改
#启停
service jenkins start/stop/restart

摘要

坐标系统被广泛用于地图、地理信息、气象、遥感、航海等多个不同领域,为了方便不同领域的人们更好更快的理解地理位置之间的关系,便有了各种不同的坐标系来描述物质存在的空间位置关系。一般分为两大类坐标系,地理坐标系(Geodetic Coordinate System, GCS)与投影坐标系(Projected Coordinate System, PCS)。

地理坐标系

简单来说,地理坐标系就是使用经纬度来描述空间位置关系的坐标系统。

地理坐标系的由来

  1. 大地水准面(Geoid)

    平均海水面重合并延申到大陆内部形成的水准面,是一个略有起伏的不规则曲面。

  2. 旋转椭球体(Ellipsoid)

    旋转椭球体是由经线绕地轴回转而成的。旋转椭球体用作大地测量中对地球形状的抽象。

    旋转椭球体的参数 :(赤道半径-极半径)/ 赤道半径 = 扁率

  3. 以旋转椭球体为参考建立的坐标系统称为地理坐标系。

地理坐标系的种类

  1. 地心坐标系(Geocentric Coordinate System)

    以地球质心点为旋转椭球体的原点形成的坐标系统称为地心坐标系。

    常见地心坐标系

    • WGS84(World Geodetic System 1984),是为GPS全球定位系统使用而建立的坐标系。

    • CGCS2000(China Geodetic Coordinate System),原点为包括海洋和大气的整个地球的质量中心,是为中国北斗定位系统使用建立的坐标系统。

  2. 参心坐标系(Reference-Ellipsoid-Centric Coordinate System)

    为了适用于国家和区域,通过缩放或移动椭球体的原点使椭球面与该国家和地区尽量重合,以便在此区域减少高程误差,这样不以地球质心为椭球体圆心的坐标系称之为参心坐标系,又称为协议坐标系。

    常见参心坐标系

    Beijng54(克拉索夫斯基椭球体)

    Xian80(IAG75椭球体)

投影坐标系

由于地理坐标系由经纬度表示,无法计算面积。故产生了投影坐标系,以米等长度单位表示其位置。

投影坐标系的由来

  1. 简单理解就是将地球表面以某种投影方式投影到平面坐标内。

投影坐标系的种类

  1. 高斯克吕格(Gauss Kruger)== 横轴墨卡托(Transverse Mercator)

    投影面是椭圆柱面,圆柱轴心与地轴垂直,投影面与椭球体相切,相切处是投影中心线。

    常见的有

    • CGCS2000 3 Degree GK CM 102E
    • Xian1980 3 Degree GK Zone 40

    images

  2. 墨卡托(Mercator)

    正轴的墨卡托投影,圆柱轴心与地球平行。

  3. 通用横轴墨卡托(Universal Transverse Mercator,UTM)

    与高斯克吕格相似,高斯克吕格的投影面是与椭球面相切的,而此投影是与之相割,割于80°S和84°N。

  4. 兰伯特(Lambert)

    兰伯特投影是一种等角圆锥投影,如图所示,投影面是一个圆锥,根据圆锥面和椭球面的关系可以分为切圆和割圆两种,按照圆锥与地轴方向可以分为正轴,横轴和协轴。我国 1:5千到1:100万地形图中,常采用此种投影。

    images

  5. 阿尔伯斯(Albers)

    又名正轴等积割圆锥投影。就是圆锥的轴和地球椭球的旋转轴重合,圆锥面与椭球面相割时,保证投影面积和实际面积相同的情况下的投影。

    images

投影的表示

Proj表示法

变换莫测的this指向

  • 事件调用环境
    谁触发事件,函数里面的this就指向谁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
    .box{
    width: 100px;
    height: 100px;
    background: #0A8CD2;
    left: 0;
    transition: 1s;
    position: relative;
    }
    </style>
    </head>
    <body>
    <div class="box"></div>
    <script>
    let box = document.querySelector('.box');
    box.onclick = move;
    function move(){
    this.style.left = '100px';
    }

    console.log(this);
    </script>
    </body>
    </html>
  • 全局环境

    如上所示全局环境下在浏览器中的this指向window。
    在Node环境中如下图所示

    this_node

在/etc/systemd/system下创建gateway.service服务文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=gateway

[Service]
WorkingDirectory=/var/lib/jenkins/workspace/netcore/Ic.GateWay/bin/Debug/netcoreapp2.2/publish
ExecStart=/usr/bin/dotnet /var/lib/jenkins/workspace/netcore/Ic.GateWay/bin/Debug/netcoreapp2.2/publish/Ic.GateWay.dll --ip * --port 5200
Restart=always
RestartSec=25
SyslogIdentifier=gateway
User=root
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target

启动:systemctl start my-demo.service
结束:systemctl stop my-demo.service
重启:systemctl restart my-demo.service
状态:systemctl status my-demo.service
查看服务状态: systemctl status network.service
列出所有可用单元:systemctl list-unit-files
列出所有运行中单元:systemctl list-units
列出所有失败单元:systemctl --failed
重新载入已修改的服务 systemctl daemon-reload